summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--.github/NPTest.cache4
-rw-r--r--.github/monitoring-plugins.spec31
-rw-r--r--.github/os_detect.sh10
-rwxr-xr-x.github/prepare_debian.sh7
-rw-r--r--.github/workflows/codeql-analysis.yml20
-rw-r--r--.github/workflows/spellcheck.yml3
-rw-r--r--.github/workflows/test-next.yml20
-rw-r--r--.github/workflows/test.yml19
-rw-r--r--.gitignore21
-rw-r--r--Makefile.am3
-rw-r--r--NPTest.pm4
-rw-r--r--REQUIREMENTS4
-rw-r--r--ROADMAP117
-rw-r--r--configure.ac78
-rw-r--r--lib/Makefile.am19
-rw-r--r--lib/extra_opts.c55
-rw-r--r--lib/maxfd.c6
-rw-r--r--lib/monitoringplug.h7
-rw-r--r--lib/output.c636
-rw-r--r--lib/output.h117
-rw-r--r--lib/parse_ini.c239
-rw-r--r--lib/perfdata.c649
-rw-r--r--lib/perfdata.h219
-rw-r--r--lib/states.h71
-rw-r--r--lib/tests/Makefile.am6
-rw-r--r--lib/tests/test_base64.c271
-rw-r--r--lib/tests/test_cmd.c41
-rwxr-xr-xlib/tests/test_disk.t6
-rw-r--r--lib/tests/test_generic_output.c317
-rw-r--r--lib/tests/test_generic_output.t6
-rw-r--r--lib/tests/test_ini1.c54
-rw-r--r--lib/tests/test_opts1.c51
-rw-r--r--lib/tests/test_opts2.c86
-rw-r--r--lib/tests/test_tcp.c28
-rw-r--r--lib/tests/test_utils.c190
-rw-r--r--lib/thresholds.c71
-rw-r--r--lib/thresholds.h31
-rw-r--r--lib/utils_base.c496
-rw-r--r--lib/utils_base.h51
-rw-r--r--lib/utils_cmd.c198
-rw-r--r--lib/utils_cmd.h10
-rw-r--r--lib/utils_disk.c255
-rw-r--r--lib/utils_disk.h48
-rw-r--r--lib/utils_tcp.c41
-rw-r--r--lib/utils_tcp.h4
-rw-r--r--lib/vendor/cJSON/cJSON.c3165
-rw-r--r--lib/vendor/cJSON/cJSON.h306
-rwxr-xr-xopttest.pl50
-rw-r--r--plugins-root/Makefile.am7
-rw-r--r--plugins-root/check_dhcp.c1021
-rw-r--r--plugins-root/check_dhcp.d/config.h50
-rw-r--r--plugins-root/check_icmp.c2922
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.c134
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.h68
-rw-r--r--plugins-root/check_icmp.d/config.h115
-rw-r--r--plugins-root/pst3.c441
-rw-r--r--plugins-root/t/check_dhcp.t24
-rw-r--r--plugins-root/t/check_icmp.t58
-rw-r--r--[-rwxr-xr-x]plugins-scripts/check_sensors.sh1
-rw-r--r--plugins/Makefile.am80
-rw-r--r--plugins/check_apt.c388
-rw-r--r--plugins/check_apt.d/config.h46
-rw-r--r--plugins/check_by_ssh.c439
-rw-r--r--plugins/check_by_ssh.d/config.h58
-rw-r--r--plugins/check_cluster.c206
-rw-r--r--plugins/check_cluster.d/config.h33
-rw-r--r--plugins/check_curl.c2886
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c1267
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h124
-rw-r--r--plugins/check_curl.d/config.h116
-rw-r--r--plugins/check_dbi.c495
-rw-r--r--plugins/check_dbi.d/config.h63
-rw-r--r--plugins/check_dig.c189
-rw-r--r--plugins/check_dig.d/config.h40
-rw-r--r--plugins/check_disk.c1447
-rw-r--r--plugins/check_disk.d/utils_disk.c528
-rw-r--r--plugins/check_disk.d/utils_disk.h160
-rw-r--r--plugins/check_dns.c464
-rw-r--r--plugins/check_dns.d/config.h34
-rw-r--r--plugins/check_dummy.c15
-rw-r--r--plugins/check_fping.c376
-rw-r--r--plugins/check_fping.d/config.h81
-rw-r--r--plugins/check_game.c273
-rw-r--r--plugins/check_game.d/config.h30
-rw-r--r--plugins/check_hpjd.c186
-rw-r--r--plugins/check_hpjd.d/config.h25
-rw-r--r--plugins/check_http.c3581
-rw-r--r--plugins/check_ide_smart.c90
-rw-r--r--plugins/check_ldap.c616
-rw-r--r--plugins/check_ldap.d/config.h60
-rw-r--r--plugins/check_load.c662
-rw-r--r--plugins/check_load.d/config.h30
-rw-r--r--plugins/check_mrtg.c226
-rw-r--r--plugins/check_mrtg.d/config.h36
-rw-r--r--plugins/check_mrtgtraf.c146
-rw-r--r--plugins/check_mrtgtraf.d/config.h30
-rw-r--r--plugins/check_mysql.c834
-rw-r--r--plugins/check_mysql.d/config.h58
-rw-r--r--plugins/check_mysql_query.c156
-rw-r--r--plugins/check_mysql_query.d/config.h36
-rw-r--r--plugins/check_nagios.c168
-rw-r--r--plugins/check_nagios.d/config.h19
-rw-r--r--plugins/check_nt.c618
-rw-r--r--plugins/check_nt.d/config.h53
-rw-r--r--plugins/check_ntp.c756
-rw-r--r--plugins/check_ntp_peer.c487
-rw-r--r--plugins/check_ntp_peer.d/config.h67
-rw-r--r--plugins/check_ntp_time.c299
-rw-r--r--plugins/check_ntp_time.d/config.h28
-rw-r--r--plugins/check_nwstat.c1527
-rw-r--r--plugins/check_overcr.c417
-rw-r--r--plugins/check_pgsql.c275
-rw-r--r--plugins/check_pgsql.d/config.h61
-rw-r--r--plugins/check_ping.c580
-rw-r--r--plugins/check_ping.d/config.h46
-rw-r--r--plugins/check_procs.c1178
-rw-r--r--plugins/check_procs.d/config.h75
-rw-r--r--plugins/check_radius.c553
-rw-r--r--plugins/check_radius.d/config.h42
-rw-r--r--plugins/check_real.c269
-rw-r--r--plugins/check_real.d/config.h37
-rw-r--r--plugins/check_smtp.c1190
-rw-r--r--plugins/check_smtp.d/config.h92
-rw-r--r--plugins/check_snmp.c1767
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.c934
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.h71
-rw-r--r--plugins/check_snmp.d/config.h81
-rw-r--r--plugins/check_ssh.c267
-rw-r--r--plugins/check_ssh.d/config.h29
-rw-r--r--plugins/check_swap.c152
-rw-r--r--plugins/check_swap.d/check_swap.h10
-rw-r--r--plugins/check_swap.d/swap.c124
-rw-r--r--plugins/check_tcp.c872
-rw-r--r--plugins/check_tcp.d/config.h84
-rw-r--r--plugins/check_time.c198
-rw-r--r--plugins/check_time.d/config.h42
-rw-r--r--plugins/check_ups.c309
-rw-r--r--plugins/check_ups.d/config.h54
-rw-r--r--plugins/check_users.c266
-rw-r--r--plugins/check_users.d/config.h20
-rw-r--r--plugins/check_users.d/users.c169
-rw-r--r--plugins/check_users.d/users.h18
-rw-r--r--plugins/common.h190
-rw-r--r--plugins/negate.c142
-rw-r--r--plugins/negate.d/config.h24
-rw-r--r--plugins/netutils.c309
-rw-r--r--plugins/netutils.h152
-rw-r--r--plugins/picohttpparser/picohttpparser.c243
-rw-r--r--plugins/picohttpparser/picohttpparser.h31
-rw-r--r--plugins/popen.c69
-rw-r--r--plugins/popen.h20
-rw-r--r--plugins/runcmd.c78
-rw-r--r--plugins/runcmd.h47
-rw-r--r--plugins/sslutils.c256
-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.c (renamed from lib/tests/test_disk.c)189
-rwxr-xr-xplugins/tests/test_check_disk.t6
-rw-r--r--plugins/tests/test_check_snmp.c175
-rwxr-xr-xplugins/tests/test_check_snmp.t6
-rw-r--r--plugins/tests/test_check_swap.c4
-rw-r--r--plugins/urlize.c36
-rw-r--r--plugins/utils.c298
-rw-r--r--plugins/utils.h159
-rw-r--r--tap/tap.c25
-rw-r--r--tap/tap.h43
-rw-r--r--tools/mini_epn.c96
-rwxr-xr-xtools/opttest.pl74
-rwxr-xr-xtools/tinderbox_build290
189 files changed, 28907 insertions, 18926 deletions
diff --git a/.clang-format b/.clang-format
index ca411edd..0ff68114 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,7 +4,7 @@ TabWidth: 4
4AllowShortIfStatementsOnASingleLine: false 4AllowShortIfStatementsOnASingleLine: false
5BreakBeforeBraces: Attach 5BreakBeforeBraces: Attach
6AlignConsecutiveMacros: true 6AlignConsecutiveMacros: true
7ColumnLimit: 140 7ColumnLimit: 100
8IndentPPDirectives: AfterHash 8IndentPPDirectives: AfterHash
9SortIncludes: Never 9SortIncludes: Never
10AllowShortEnumsOnASingleLine: false 10AllowShortEnumsOnASingleLine: false
diff --git a/.github/NPTest.cache b/.github/NPTest.cache
index d488d1b9..6b463e74 100644
--- a/.github/NPTest.cache
+++ b/.github/NPTest.cache
@@ -38,8 +38,8 @@
38 'NP_MYSQL_LOGIN_DETAILS' => '-u root -d test', 38 'NP_MYSQL_LOGIN_DETAILS' => '-u root -d test',
39 'NP_MYSQL_SERVER' => 'localhost', 39 'NP_MYSQL_SERVER' => 'localhost',
40 'NP_MYSQL_SOCKET' => '/var/run/mysqld/mysqld.sock', 40 'NP_MYSQL_SOCKET' => '/var/run/mysqld/mysqld.sock',
41 'NP_MYSQL_WITH_SLAVE' => '', 41 'NP_MYSQL_WITH_REPLICA' => '',
42 'NP_MYSQL_WITH_SLAVE_LOGIN' => '', 42 'NP_MYSQL_WITH_REPLICA_LOGIN' => '',
43 'NP_NO_NTP_SERVICE' => 'localhost', 43 'NP_NO_NTP_SERVICE' => 'localhost',
44 'NP_PORT_TCP_PROXY' => '3128', 44 'NP_PORT_TCP_PROXY' => '3128',
45 'NP_SMB_SHARE' => '', 45 'NP_SMB_SHARE' => '',
diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec
index 5cae3e59..ce22606b 100644
--- a/.github/monitoring-plugins.spec
+++ b/.github/monitoring-plugins.spec
@@ -88,6 +88,9 @@ BuildRequires: postgresql-devel
88# check_radius 88# check_radius
89BuildRequires: radcli-devel 89BuildRequires: radcli-devel
90 90
91# check_snmp
92BuildRequires: net-snmp-devel
93
91%description 94%description
92Common files for Monitoring Plugins 95Common files for Monitoring Plugins
93 96
@@ -191,9 +194,7 @@ Requires: %{name}-nt
191Requires: %{name}-ntp 194Requires: %{name}-ntp
192Requires: %{name}-ntp_peer 195Requires: %{name}-ntp_peer
193Requires: %{name}-ntp_time 196Requires: %{name}-ntp_time
194Requires: %{name}-nwstat
195Requires: %{name}-oracle 197Requires: %{name}-oracle
196Requires: %{name}-overcr
197Requires: %{name}-pgsql 198Requires: %{name}-pgsql
198Requires: %{name}-ping 199Requires: %{name}-ping
199Requires: %{name}-procs 200Requires: %{name}-procs
@@ -703,19 +704,6 @@ Provides check_ntp_time of the Monitoring Plugins.
703 704
704 705
705 706
706# check_nwstat
707%package nwstat
708Summary: Monitoring Plugins - check_nwstat
709Requires: %{name} = %{version}-%{release}
710
711%description nwstat
712Provides check_nwstat of the Monitoring Plugins.
713
714%files nwstat
715%{plugindir}/check_nwstat
716
717
718
719# check_oracle 707# check_oracle
720%package oracle 708%package oracle
721Summary: Monitoring Plugins - check_oracle 709Summary: Monitoring Plugins - check_oracle
@@ -729,19 +717,6 @@ Provides check_oracle of the Monitoring Plugins.
729 717
730 718
731 719
732# check_overcr
733%package overcr
734Summary: Monitoring Plugins - check_overcr
735Requires: %{name} = %{version}-%{release}
736
737%description overcr
738Provides check_overcr of the Monitoring Plugins.
739
740%files overcr
741%{plugindir}/check_overcr
742
743
744
745# check_pgsql 720# check_pgsql
746%package pgsql 721%package pgsql
747Summary: Monitoring Plugins - check_pgsql 722Summary: Monitoring Plugins - check_pgsql
diff --git a/.github/os_detect.sh b/.github/os_detect.sh
index ee9c145d..3c5956de 100644
--- a/.github/os_detect.sh
+++ b/.github/os_detect.sh
@@ -1,10 +1,17 @@
1#!/bin/sh -e 1#!/bin/sh -e
2
3. /etc/os-release
4
2# workaround for really bare-bones Archlinux containers: 5# workaround for really bare-bones Archlinux containers:
3if [ -x "$(command -v pacman)" ]; then 6if [ -x "$(command -v pacman)" ]; then
4 pacman --noconfirm -Sy 7 pacman --noconfirm -Sy
5 pacman --noconfirm -S grep gawk sed 8 pacman --noconfirm -S grep gawk sed
6fi 9fi
7 10
11if [ ${ID} == "fedora" -a ${VERSION_ID} -gt 41 ]; then
12 dnf install -y gawk
13fi
14
8os_release_file= 15os_release_file=
9if [ -s "/etc/os-release" ]; then 16if [ -s "/etc/os-release" ]; then
10 os_release_file="/etc/os-release" 17 os_release_file="/etc/os-release"
@@ -15,4 +22,7 @@ else
15 return 1 22 return 1
16fi 23fi
17export distro_id=$(grep '^ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') 24export distro_id=$(grep '^ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g')
25export version_id=$(grep '^VERSION_ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g')
18export platform_id=$(grep '^PLATFORM_ID=' /etc/os-release|awk -F = '{print $2}'|sed 's/\"//g'| cut -d":" -f2) 26export platform_id=$(grep '^PLATFORM_ID=' /etc/os-release|awk -F = '{print $2}'|sed 's/\"//g'| cut -d":" -f2)
27# Fedora dropped PLATFORM_ID: https://fedoraproject.org/wiki/Changes/Drop_PLATFORM_ID?#Drop_PLATFORM_ID
28if [ -z $platform_id ]; then export platform_id=$(echo ${distro_id:0:1}${version_id}); fi
diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh
index 3640e500..cffe98c5 100755
--- a/.github/prepare_debian.sh
+++ b/.github/prepare_debian.sh
@@ -24,6 +24,7 @@ apt-get -y install perl \
24 libpq-dev \ 24 libpq-dev \
25 libradcli-dev \ 25 libradcli-dev \
26 libnet-snmp-perl \ 26 libnet-snmp-perl \
27 libsnmp-dev \
27 procps \ 28 procps \
28 libdbi0-dev \ 29 libdbi0-dev \
29 libdbd-sqlite3 \ 30 libdbd-sqlite3 \
@@ -59,9 +60,11 @@ apt-get -y install perl \
59 mariadb-server \ 60 mariadb-server \
60 mariadb-client \ 61 mariadb-client \
61 libmariadb-dev \ 62 libmariadb-dev \
63 libmariadb-dev-compat \
62 cron \ 64 cron \
63 iputils-ping \ 65 iputils-ping \
64 iproute2 66 iproute2 \
67 libjson-perl
65 68
66# remove ipv6 interface from hosts 69# remove ipv6 interface from hosts
67sed '/^::1/d' /etc/hosts > /tmp/hosts 70sed '/^::1/d' /etc/hosts > /tmp/hosts
@@ -109,6 +112,8 @@ mkdir -p /var/lib/snmp/mib_indexes
109sed -e 's/^agentaddress.*/agentaddress 127.0.0.1/' -i /etc/snmp/snmpd.conf 112sed -e 's/^agentaddress.*/agentaddress 127.0.0.1/' -i /etc/snmp/snmpd.conf
110service snmpd start 113service snmpd start
111 114
115sed 's/^mibs ://' -i /etc/snmp/snmp.conf
116
112# start cron, will be used by check_nagios 117# start cron, will be used by check_nagios
113cron 118cron
114 119
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index c402e0cf..e01aa5fc 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -13,6 +13,7 @@
13name: "CodeQL" 13name: "CodeQL"
14 14
15on: 15on:
16 workflow_dispatch: {}
16 push: 17 push:
17 branches: [master] 18 branches: [master]
18 pull_request: 19 pull_request:
@@ -40,7 +41,7 @@ jobs:
40 41
41 steps: 42 steps:
42 - name: Checkout repository 43 - name: Checkout repository
43 uses: actions/checkout@v4 44 uses: actions/checkout@v5
44 45
45 # Initializes the CodeQL tools for scanning. 46 # Initializes the CodeQL tools for scanning.
46 - name: Initialize CodeQL 47 - name: Initialize CodeQL
@@ -56,9 +57,20 @@ jobs:
56 run: | 57 run: |
57 sudo apt update 58 sudo apt update
58 sudo apt-get install -y --no-install-recommends m4 gettext automake autoconf make build-essential 59 sudo apt-get install -y --no-install-recommends m4 gettext automake autoconf make build-essential
59 sudo apt-get install -y --no-install-recommends perl autotools-dev libdbi-dev libldap2-dev libpq-dev \ 60 sudo apt-get install -y --no-install-recommends perl \
60 libmysqlclient-dev libradcli-dev libkrb5-dev libdbi0-dev \ 61 autotools-dev \
61 libdbd-sqlite3 libssl-dev libcurl4-openssl-dev liburiparser-dev 62 libdbi-dev \
63 libldap2-dev \
64 libpq-dev \
65 libmysqlclient-dev \
66 libradcli-dev \
67 libkrb5-dev \
68 libdbi0-dev \
69 libdbd-sqlite3 \
70 libssl-dev \
71 libcurl4-openssl-dev \
72 liburiparser-dev \
73 libsnmp-dev
62 74
63 - name: Configure build 75 - name: Configure build
64 run: | 76 run: |
diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml
index 72f7c7eb..14b82781 100644
--- a/.github/workflows/spellcheck.yml
+++ b/.github/workflows/spellcheck.yml
@@ -2,6 +2,7 @@
2name: Spellcheck 2name: Spellcheck
3 3
4on: 4on:
5 workflow_dispatch: {}
5 # Run for pushes on any branch 6 # Run for pushes on any branch
6 push: 7 push:
7 branches: 8 branches:
@@ -17,7 +18,7 @@ jobs:
17 runs-on: ubuntu-latest 18 runs-on: ubuntu-latest
18 steps: 19 steps:
19 - name: Checkout 20 - name: Checkout
20 uses: actions/checkout@v4 21 uses: actions/checkout@v5
21 - name: Codespell 22 - name: Codespell
22 uses: codespell-project/actions-codespell@v2 23 uses: codespell-project/actions-codespell@v2
23 with: 24 with:
diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml
index 81240759..0e69c251 100644
--- a/.github/workflows/test-next.yml
+++ b/.github/workflows/test-next.yml
@@ -2,7 +2,13 @@
2name: Tests Debian:Testing and Fedora:Rawhide 2name: Tests Debian:Testing and Fedora:Rawhide
3 3
4on: 4on:
5 workflow_dispatch: {} 5 workflow_dispatch:
6 inputs:
7 debug_enabled:
8 type: boolean
9 description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
10 required: false
11 default: false
6 push: 12 push:
7 branches-ignore: 13 branches-ignore:
8 - '*' 14 - '*'
@@ -24,7 +30,10 @@ jobs:
24 prepare: .github/prepare_debian.sh 30 prepare: .github/prepare_debian.sh
25 steps: 31 steps:
26 - name: Git clone repository 32 - name: Git clone repository
27 uses: actions/checkout@v4 33 uses: actions/checkout@v5
34 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
35 uses: mxschmitt/action-tmate@v3
36 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
28 - name: Run the tests on ${{ matrix.distro }} 37 - name: Run the tests on ${{ matrix.distro }}
29 run: | 38 run: |
30 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol 39 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol
@@ -38,7 +47,7 @@ jobs:
38 ${{ matrix.distro }} \ 47 ${{ matrix.distro }} \
39 /bin/sh -c '${{ matrix.prepare }} && \ 48 /bin/sh -c '${{ matrix.prepare }} && \
40 tools/setup && \ 49 tools/setup && \
41 ./configure --enable-libtap --with-ipv6=no && \ 50 ./configure --enable-libtap && \
42 make && \ 51 make && \
43 make test && \ 52 make test && \
44 make dist && \ 53 make dist && \
@@ -59,7 +68,10 @@ jobs:
59 - {"distro": "fedora:rawhide", "build": ".github/mock.sh"} 68 - {"distro": "fedora:rawhide", "build": ".github/mock.sh"}
60 steps: 69 steps:
61 - name: Git clone repository 70 - name: Git clone repository
62 uses: actions/checkout@v4 71 uses: actions/checkout@v5
72 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
73 uses: mxschmitt/action-tmate@v3
74 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
63 - name: Run the tests on ${{ matrix.distro }} 75 - name: Run the tests on ${{ matrix.distro }}
64 run: | 76 run: |
65 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol 77 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 77ca6585..1ac8aaf3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -2,6 +2,13 @@
2name: Tests 2name: Tests
3 3
4on: 4on:
5 workflow_dispatch:
6 inputs:
7 debug_enabled:
8 type: boolean
9 description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
10 required: false
11 default: false
5 push: 12 push:
6 branches: 13 branches:
7 - '*' 14 - '*'
@@ -21,7 +28,10 @@ jobs:
21 prepare: .github/prepare_debian.sh 28 prepare: .github/prepare_debian.sh
22 steps: 29 steps:
23 - name: Git clone repository 30 - name: Git clone repository
24 uses: actions/checkout@v4 31 uses: actions/checkout@v5
32 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
33 uses: mxschmitt/action-tmate@v3
34 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
25 - name: Run the tests on ${{ matrix.distro }} 35 - name: Run the tests on ${{ matrix.distro }}
26 run: | 36 run: |
27 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol 37 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol
@@ -35,7 +45,7 @@ jobs:
35 ${{ matrix.distro }} \ 45 ${{ matrix.distro }} \
36 /bin/sh -c '${{ matrix.prepare }} && \ 46 /bin/sh -c '${{ matrix.prepare }} && \
37 tools/setup && \ 47 tools/setup && \
38 ./configure --enable-libtap --with-ipv6=no && \ 48 ./configure --enable-libtap && \
39 make && \ 49 make && \
40 make test && \ 50 make test && \
41 make dist && \ 51 make dist && \
@@ -59,7 +69,10 @@ jobs:
59# - {"distro": "oraclelinux:9", "build": ".github/mock.sh"} 69# - {"distro": "oraclelinux:9", "build": ".github/mock.sh"}
60 steps: 70 steps:
61 - name: Git clone repository 71 - name: Git clone repository
62 uses: actions/checkout@v4 72 uses: actions/checkout@v5
73 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
74 uses: mxschmitt/action-tmate@v3
75 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
63 - name: Run the tests on ${{ matrix.distro }} 76 - name: Run the tests on ${{ matrix.distro }}
64 run: | 77 run: |
65 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol 78 docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol
diff --git a/.gitignore b/.gitignore
index 0b5028ed..00e19d52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,6 +103,8 @@ NP-VERSION-FILE
103/lib/libmonitoringplug.a 103/lib/libmonitoringplug.a
104/lib/Makefile 104/lib/Makefile
105/lib/Makefile.in 105/lib/Makefile.in
106/lib/vendor/cJSON/.deps
107/lib/vendor/cJSON/.dirstamp
106 108
107# /lib/tests/ 109# /lib/tests/
108/lib/tests/base64.Po 110/lib/tests/base64.Po
@@ -112,7 +114,6 @@ NP-VERSION-FILE
112/lib/tests/Makefile.in 114/lib/tests/Makefile.in
113/lib/tests/test_base64 115/lib/tests/test_base64
114/lib/tests/test_cmd 116/lib/tests/test_cmd
115/lib/tests/test_disk
116/lib/tests/test_tcp 117/lib/tests/test_tcp
117/lib/tests/test_utils 118/lib/tests/test_utils
118/lib/tests/utils_base.Po 119/lib/tests/utils_base.Po
@@ -151,6 +152,8 @@ NP-VERSION-FILE
151/plugins/check_dbi 152/plugins/check_dbi
152/plugins/check_dig 153/plugins/check_dig
153/plugins/check_disk 154/plugins/check_disk
155plugins/check_disk.d/.deps/
156plugins/check_disk.d/.dirstamp
154/plugins/check_dns 157/plugins/check_dns
155/plugins/check_dummy 158/plugins/check_dummy
156/plugins/check_fping 159/plugins/check_fping
@@ -175,8 +178,6 @@ NP-VERSION-FILE
175/plugins/check_ntp 178/plugins/check_ntp
176/plugins/check_ntp_peer 179/plugins/check_ntp_peer
177/plugins/check_ntp_time 180/plugins/check_ntp_time
178/plugins/check_nwstat
179/plugins/check_overcr
180/plugins/check_pgsql 181/plugins/check_pgsql
181/plugins/check_ping 182/plugins/check_ping
182/plugins/check_pop 183/plugins/check_pop
@@ -196,6 +197,8 @@ NP-VERSION-FILE
196/plugins/check_udp 197/plugins/check_udp
197/plugins/check_ups 198/plugins/check_ups
198/plugins/check_users 199/plugins/check_users
200/plugins/check_users.d/.deps
201/plugins/check_users.d/.dirstamp
199/plugins/check_vsz 202/plugins/check_vsz
200/plugins/config.h 203/plugins/config.h
201/plugins/config.h.in 204/plugins/config.h.in
@@ -221,7 +224,7 @@ NP-VERSION-FILE
221/plugins/tests/Makefile 224/plugins/tests/Makefile
222/plugins/tests/Makefile.in 225/plugins/tests/Makefile.in
223/plugins/tests/test_utils 226/plugins/tests/test_utils
224/plugins/tests/test_disk 227/plugins/tests/test_check_disk
225/plugins/tests/test_check_swap 228/plugins/tests/test_check_swap
226/plugins/tests/.deps 229/plugins/tests/.deps
227/plugins/tests/.dirstamp 230/plugins/tests/.dirstamp
@@ -230,6 +233,14 @@ NP-VERSION-FILE
230/plugins/check_swap.d/.deps 233/plugins/check_swap.d/.deps
231/plugins/check_swap.d/.dirstamp 234/plugins/check_swap.d/.dirstamp
232 235
236# /plugins/check_snmp.d
237/plugins/check_snmp.d/.deps
238/plugins/check_snmp.d/.dirstamp
239
240# /plugins/check_curl.d
241/plugins/check_curl.d/.deps
242/plugins/check_curl.d/.dirstamp
243
233# /plugins-root/ 244# /plugins-root/
234/plugins-root/.deps 245/plugins-root/.deps
235/plugins-root/.libs 246/plugins-root/.libs
@@ -238,6 +249,8 @@ NP-VERSION-FILE
238/plugins-root/check_dhcp 249/plugins-root/check_dhcp
239/plugins-root/check_icmp 250/plugins-root/check_icmp
240/plugins-root/pst3 251/plugins-root/pst3
252/plugins-root/check_icmp.d/.dirstamp
253/plugins-root/check_icmp.d/.deps
241 254
242# /plugins-scripts/ 255# /plugins-scripts/
243/plugins-scripts/Makefile 256/plugins-scripts/Makefile
diff --git a/Makefile.am b/Makefile.am
index 5959f70f..f8e3de45 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,8 +7,7 @@ EXTRA_DIST = config.rpath \
7 NP-VERSION-GEN REQUIREMENTS SUPPORT THANKS \ 7 NP-VERSION-GEN REQUIREMENTS SUPPORT THANKS \
8 NPTest.pm pkg \ 8 NPTest.pm pkg \
9 config_test/Makefile config_test/run_tests config_test/child_test.c \ 9 config_test/Makefile config_test/run_tests config_test/child_test.c \
10 perlmods tools/build_perl_modules \ 10 perlmods tools/build_perl_modules
11 tools/tinderbox_build
12 11
13ACLOCAL_AMFLAGS = -I gl/m4 -I m4 12ACLOCAL_AMFLAGS = -I gl/m4 -I m4
14 13
diff --git a/NPTest.pm b/NPTest.pm
index 9b25ac3e..1c008589 100644
--- a/NPTest.pm
+++ b/NPTest.pm
@@ -15,6 +15,8 @@ use warnings;
15use Cwd; 15use Cwd;
16use File::Basename; 16use File::Basename;
17 17
18use JSON;
19
18use IO::File; 20use IO::File;
19use Data::Dumper; 21use Data::Dumper;
20 22
@@ -617,6 +619,8 @@ sub testCmd {
617 chomp $output; 619 chomp $output;
618 $object->output($output); 620 $object->output($output);
619 621
622 eval { $object->{'mp_test_result'} = decode_json($output) };
623
620 alarm(0); 624 alarm(0);
621 625
622 my ($pkg, $file, $line) = caller(0); 626 my ($pkg, $file, $line) = caller(0);
diff --git a/REQUIREMENTS b/REQUIREMENTS
index f3b1c01d..551fdb1a 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -87,10 +87,6 @@ check_ifstatus/check_ifoperstatus
87 - Requires Net::SNMP perl module 87 - Requires Net::SNMP perl module
88 http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/ 88 http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/
89 89
90check_nwstat:
91 - Requires MRTGEXT NLM for Novell Servers
92 http://forge.novell.com/modules/xfmod/project/?mrtgext
93
94check_nt: 90check_nt:
95 - Requires NSClient to run on the NT server to monitor 91 - Requires NSClient to run on the NT server to monitor
96 http://nsclient.ready2run.nl/ 92 http://nsclient.ready2run.nl/
diff --git a/ROADMAP b/ROADMAP
deleted file mode 100644
index 6378ec74..00000000
--- a/ROADMAP
+++ /dev/null
@@ -1,117 +0,0 @@
1
2Releases in the 1.3 series will be for development. Version 1.4.0 will
3be the next full production release. I am not planning on dates right now,
4but you can expect maintennence releases for 1.2.9 as well.
5
6With that done, it's time to figure out what we are doing for release
71.3 development. I have a few ideas. Maybe others do as well.
8
9DOCUMENTATION:
10 We pretty much have decided that we will doing something along
11 the lines of a literate programming model. So far, we have site
12 documentation in DocBook. I have some ideas here, which I will
13 discuss in a separate thread.
14
15
16
17OPTION PROCESSING:
18 I believe we can remove reverse compatibility for non-Getopt
19 option specifications. For example, 'check_ping 1 2 3 4 -p 2'
20 would not be supported anymore. Support for this is a hack,
21 and making it portable is bug-prone. We should also review
22 standardization of our options -- the current list is a little
23 ad hoc, it should be nailed down. Details in a separate thread.
24
25Also,
26
27 check_http -p 443 --ssl www.infoplease.com
28
29should be fine, but if the getopt in use does not natively support it,
30things like
31
32 check_http www.infoplease.com -p 443 --ssl
33
34should be trapped and result in a call to one of the usage macros
35(which print a message and then exit STATE_UNKNOWN).
36
37This means that the call_getopt() function can go away. It's an
38inconsistent mess and I'd love to ditch it. I only created it to
39satisfy people that wanted reverse compatibility and did not have
40GNU getopt.
41
42But I would like to urge that all standard plugins contain
43validate_arguments(). I think this will help convey the idea that
44validations should be done, even if we don't insist on the specific
45extent that each plugin must do that validation.
46
47This is the set of standard options I envision:
48
49Reserved:
50
51-h, --help (REQUIRED!!!!!)
52-V, --version (REQUIRED!!!!!)
53-v, --verbose
54-q, --quiet
55-t, --timeout = INTEGER (senonds)
56-c, --critical = (INT|FLOAT|RANGE|LIST)
57-w, --warning = (INT|FLOAT|RANGE|LIST)
58-H, --hostname = STRING
59-F, --file = STRING (usually input)
60-O, --output = STRING (output file)
61
62Recommended, but not reserved:
63
64-I, --ipaddress = STRING
65-C, --community = STRING
66-a, --auth(info) = STRING (authentication or password)
67-l, --logname = STRING
68-p, --password = STRING
69-P, --port = INT
70-u, --url = STRING (also --username if --url is not needed)
71
72I am suggesting that port always be '-P' (uppercase) -- we are
73currently inconsistent in that regard.
74
75I am also adding '-q' for silent running. This is totally self
76centered--I am planning to use a plugin in a cron script, and I
77don't want nightly emails.
78
79As has been the case, ranges are specified with colons, like 'i:j'
80and list are specified with commas like 'i,k' and may contain ranges
81if it makes sense to do so. Perhaps it would be good to build a
82standard list/range processing function for this task.
83
84
85Programming:
86 I would like to follow the GNU guidelines and remove all fixed
87 length character assignments, at least to the extent possible,
88 from the C-based plugins. To that end, I have made strscpy and
89 friends in utils.c -- I'd like to deploy them. I have comments
90 that there is a lot of duplicated code, and techniques used that
91 should be cleaned up. Details in a separate thread.
92
93Remote checks:
94 I have a proposal in hand to incorporate ssh check into spopen()
95 so that remote machine checks can be seamless. A nice idea, but
96 complex enough to require discussion. Another thread.
97
98I also have a wish list, and I'm sure I've forgot some items. I'll
99list mine, please respond with other items that can be put into the
100sourceforge task manager:
101
102 Indent all code in a GNU-compatible manner (indent -ts 2 -br)
103 Add RAND_seed to check_http for --ssl on systems without /dev/random
104 Add regex filtering to check_procs --args option
105 Add working procs syntax for AIX check_procs
106 Allow check_disk to exclude non-local disks
107 Add md5 checksumming to check_http
108 Complete unification of check_tcp and friends
109 Add SSL in a general way to check_tcp and friends
110 Patches to check_log from Joonas
111 Add calculator engine and snmpwalk to check_snmp
112 Is there a bug in check_oracle
113 Are there outstanding bugs in check_snmp
114 Change check_http --onredirect to default as STATE_UNKNOWN
115 Create plugin to check tftp servers
116 Create plugin wrapper to invert error status
117
diff --git a/configure.ac b/configure.ac
index ef3d26e2..705183a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,10 +181,10 @@ fi
181 181
182# Finally, define tests if we use libtap 182# Finally, define tests if we use libtap
183if test "$enable_libtap" = "yes" ; then 183if test "$enable_libtap" = "yes" ; then
184 EXTRA_TEST="test_utils test_disk test_tcp test_cmd test_base64" 184 EXTRA_TEST="test_utils test_tcp test_cmd test_base64"
185 AC_SUBST(EXTRA_TEST) 185 AC_SUBST(EXTRA_TEST)
186 186
187 EXTRA_PLUGIN_TESTS="tests/test_check_swap" 187 EXTRA_PLUGIN_TESTS="tests/test_check_swap tests/test_check_disk"
188 AC_SUBST(EXTRA_PLUGIN_TESTS) 188 AC_SUBST(EXTRA_PLUGIN_TESTS)
189fi 189fi
190 190
@@ -796,7 +796,7 @@ elif ps axwo 'stat comm vsz rss user uid pid ppid etime args' 2>/dev/null | \
796then 796then
797 ac_cv_ps_varlist="[procstat,&procuid,&procpid,&procppid,&procvsz,&procrss,&procpcpu,procetime,procprog,&pos]" 797 ac_cv_ps_varlist="[procstat,&procuid,&procpid,&procppid,&procvsz,&procrss,&procpcpu,procetime,procprog,&pos]"
798 ac_cv_ps_command="$PATH_TO_PS axwo 'stat uid pid ppid vsz rss pcpu etime comm args'" 798 ac_cv_ps_command="$PATH_TO_PS axwo 'stat uid pid ppid vsz rss pcpu etime comm args'"
799 ac_cv_ps_format="%s %d %d %d %d %d %f %s %s %n" 799 ac_cv_ps_format="%s %u %d %d %d %d %f %s %s %n"
800 ac_cv_ps_cols=10 800 ac_cv_ps_cols=10
801 AC_MSG_RESULT([$ac_cv_ps_command]) 801 AC_MSG_RESULT([$ac_cv_ps_command])
802 802
@@ -1460,23 +1460,19 @@ AC_ARG_WITH(snmpget_command,
1460 ACX_HELP_STRING([--with-snmpget-command=PATH], 1460 ACX_HELP_STRING([--with-snmpget-command=PATH],
1461 [Path to snmpget command]), 1461 [Path to snmpget command]),
1462 PATH_TO_SNMPGET=$withval) 1462 PATH_TO_SNMPGET=$withval)
1463if test -n "$PATH_TO_SNMPGET"
1464then
1465 AC_DEFINE_UNQUOTED(PATH_TO_SNMPGET,"$PATH_TO_SNMPGET",[path to snmpget binary])
1466 EXTRAS="$EXTRAS check_hpjd check_snmp\$(EXEEXT)"
1467else
1468 AC_MSG_WARN([Get snmpget from http://net-snmp.sourceforge.net to make check_hpjd and check_snmp plugins])
1469fi
1470 1463
1471AC_PATH_PROG(PATH_TO_SNMPGETNEXT,snmpgetnext) 1464AC_PATH_PROG(PATH_TO_SNMPGETNEXT,snmpgetnext)
1472AC_ARG_WITH(snmpgetnext_command, 1465AC_ARG_WITH(snmpgetnext_command,
1473 ACX_HELP_STRING([--with-snmpgetnext-command=PATH], 1466 ACX_HELP_STRING([--with-snmpgetnext-command=PATH],
1474 [Path to snmpgetnext command]), 1467 [Path to snmpgetnext command]),
1475 PATH_TO_SNMPGETNEXT=$withval) 1468 PATH_TO_SNMPGETNEXT=$withval)
1476if test -n "$PATH_TO_SNMPGETNEXT" 1469
1477then 1470AS_IF([test -n "$PATH_TO_SNMPGET"], [
1478 AC_DEFINE_UNQUOTED(PATH_TO_SNMPGETNEXT,"$PATH_TO_SNMPGETNEXT",[path to snmpgetnext binary]) 1471 AC_DEFINE_UNQUOTED(PATH_TO_SNMPGET,"$PATH_TO_SNMPGET",[path to snmpget binary])
1479fi 1472 EXTRAS="$EXTRAS check_hpjd"
1473], [
1474 AC_MSG_WARN([Get snmpget from https://net-snmp.sourceforge.io/ to build the check_hpjd and check_snmp plugins])
1475])
1480 1476
1481if ( $PERL -M"Net::SNMP 3.6" -e 'exit' 2>/dev/null ) 1477if ( $PERL -M"Net::SNMP 3.6" -e 'exit' 2>/dev/null )
1482then 1478then
@@ -1486,6 +1482,16 @@ else
1486 AC_MSG_WARN([Tried $PERL - install Net::SNMP perl module if you want to use the perl snmp plugins]) 1482 AC_MSG_WARN([Tried $PERL - install Net::SNMP perl module if you want to use the perl snmp plugins])
1487fi 1483fi
1488 1484
1485dnl Check whether DES encryption is available (might not on RHEL)
1486AC_COMPILE_IFELSE(
1487 [AC_LANG_PROGRAM(
1488 [[#include <net-snmp/net-snmp-config.h>
1489 #include <net-snmp/net-snmp-includes.h>]], [[oid *foo = usmDESPrivProtocol;]]
1490 )],
1491 [AC_DEFINE(HAVE_USM_DES_PRIV_PROTOCOL,1,Define whether we have DES Privacy Protocol)],
1492 []
1493)
1494
1489AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat) 1495AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat)
1490AC_PATH_PROG(PATH_TO_QSTAT,qstat) 1496AC_PATH_PROG(PATH_TO_QSTAT,qstat)
1491AC_ARG_WITH(qstat_command, 1497AC_ARG_WITH(qstat_command,
@@ -1512,21 +1518,47 @@ then
1512fi 1518fi
1513 1519
1514AC_PATH_PROG(PATH_TO_FPING,fping) 1520AC_PATH_PROG(PATH_TO_FPING,fping)
1515AC_PATH_PROG(PATH_TO_FPING6,fping6)
1516 1521
1517AC_ARG_WITH(fping_command, 1522AC_ARG_WITH(fping_command,
1518 ACX_HELP_STRING([--with-fping-command=PATH], 1523 ACX_HELP_STRING([--with-fping-command=PATH],
1519 [Path to fping command]), PATH_TO_FPING=$withval) 1524 [Path to fping command]), PATH_TO_FPING=$withval)
1520AC_ARG_WITH(fping6_command, 1525if test -n "$PATH_TO_FPING"; then
1521 ACX_HELP_STRING([--with-fping6-command=PATH],
1522 [Path to fping6 command]), PATH_TO_FPING6=$withval)
1523
1524if test -n "$PATH_TO_FPING"
1525then
1526 AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping]) 1526 AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping])
1527 EXTRAS="$EXTRAS check_fping\$(EXEEXT)" 1527 EXTRAS="$EXTRAS check_fping\$(EXEEXT)"
1528 if test x"$with_ipv6" != xno && test -n "$PATH_TO_FPING6"; then 1528
1529 AC_DEFINE_UNQUOTED(PATH_TO_FPING6,"$PATH_TO_FPING6",[path to fping6]) 1529 if test -z "$($PATH_TO_FPING --version)" ; then
1530 AC_MSG_NOTICE([failed to get version of fping])
1531 else
1532 FPING_MAJOR_VERSION="$($PATH_TO_FPING --version | sed 's/.*fping: Version //' | sed 's/\..*//')"
1533 FPING_MINOR_VERSION="$($PATH_TO_FPING --version | sed 's/.*fping: Version //' | sed 's/.*\.//')"
1534
1535 if test $FPING_MAJOR_VERSION -eq 5 ; then
1536 if test $FPING_MINOR_VERSION -ge 3 ; then
1537 AC_DEFINE(FPING_VERSION_5_3_OR_HIGHER, "true", [fping is of version 5.3 or higher])
1538 AC_MSG_NOTICE([fping is of version 5.3 or higher])
1539 AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1540 AC_MSG_NOTICE([fping is of version 5.2 or higher])
1541 elif test $FPING_MINOR_VERSION -ge 2 ; then
1542 AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1543 AC_MSG_NOTICE([fping is of version 5.2 or higher])
1544 else
1545 AC_MSG_NOTICE([fping is of a version lower then 5.2])
1546 fi
1547
1548 elif $FPING_MAJOR_VERSION > 5 ; then
1549 AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1550 AC_MSG_NOTICE([fping is of version 5.2 or higher])
1551 AC_DEFINE(FPING_VERSION_5_3_OR_HIGHER, "true", [fping is of version 5.2 or higher])
1552 AC_MSG_NOTICE([fping is of version 5.3 or higher])
1553 fi
1554
1555 if test "`fping --version | sed 's/.*fping: Version //'`" = "5.2" ; then
1556 AC_DEFINE(FPING_VERSION, "5.2", [the version of fping available])
1557 AC_MSG_NOTICE([fping version: 5.2])
1558 elif test "`fping --version | sed 's/.*fping: Version //'`" = "5.3"; then
1559 AC_DEFINE(FPING_VERSION, "5.3", [the version of fping available])
1560 AC_MSG_NOTICE([fping version: 5.3])
1561 fi
1530 fi 1562 fi
1531else 1563else
1532 AC_MSG_WARN([Get fping from http://www.fping.com in order to make check_fping plugin]) 1564 AC_MSG_WARN([Get fping from http://www.fping.com in order to make check_fping plugin])
diff --git a/lib/Makefile.am b/lib/Makefile.am
index dc3ee893..27a08278 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,11 +4,23 @@ SUBDIRS = . tests
4 4
5noinst_LIBRARIES = libmonitoringplug.a 5noinst_LIBRARIES = libmonitoringplug.a
6 6
7AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 7AM_CPPFLAGS = \
8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
9 9
10libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c 10libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c
11EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h parse_ini.h extra_opts.h maxfd.h 11
12EXTRA_DIST = utils_base.h \
13 utils_tcp.h \
14 utils_cmd.h \
15 parse_ini.h \
16 extra_opts.h \
17 maxfd.h \
18 perfdata.h \
19 output.h \
20 thresholds.h \
21 states.h \
22 vendor/cJSON/cJSON.h \
23 monitoringplug.h
12 24
13if USE_PARSE_INI 25if USE_PARSE_INI
14libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c 26libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c
@@ -16,4 +28,3 @@ endif USE_PARSE_INI
16 28
17test test-debug: 29test test-debug:
18 cd tests && make $@ 30 cd tests && make $@
19
diff --git a/lib/extra_opts.c b/lib/extra_opts.c
index 88787336..3fe69014 100644
--- a/lib/extra_opts.c
+++ b/lib/extra_opts.c
@@ -27,27 +27,32 @@
27 27
28/* FIXME: copied from utils.h; we should move a bunch of libs! */ 28/* FIXME: copied from utils.h; we should move a bunch of libs! */
29bool is_option2(char *str) { 29bool is_option2(char *str) {
30 if (!str) 30 if (!str) {
31 return false; 31 return false;
32 else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) 32 }
33
34 if (strspn(str, "-") == 1 || strspn(str, "-") == 2) {
33 return true; 35 return true;
34 else 36 }
35 return false; 37
38 return false;
36} 39}
37 40
38/* this is the externally visible function used by plugins */ 41/* this is the externally visible function used by plugins */
39char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { 42char **np_extra_opts(int *argc, char **argv, const char *plugin_name) {
40 np_arg_list *extra_args = NULL, *ea1 = NULL, *ea_tmp = NULL;
41 char **argv_new = NULL;
42 char *argptr = NULL;
43 int i, j, optfound, argc_new, ea_num = *argc;
44
45 if (*argc < 2) { 43 if (*argc < 2) {
46 /* No arguments provided */ 44 /* No arguments provided */
47 return argv; 45 return argv;
48 } 46 }
49 47
50 for (i = 1; i < *argc; i++) { 48 np_arg_list *extra_args = NULL;
49 np_arg_list *ea1 = NULL;
50 np_arg_list *ea_tmp = NULL;
51 char *argptr = NULL;
52 int optfound;
53 size_t ea_num = (size_t)*argc;
54
55 for (int i = 1; i < *argc; i++) {
51 argptr = NULL; 56 argptr = NULL;
52 optfound = 0; 57 optfound = 0;
53 58
@@ -56,8 +61,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) {
56 /* It is a single argument with value */ 61 /* It is a single argument with value */
57 argptr = argv[i] + 13; 62 argptr = argv[i] + 13;
58 /* Delete the extra opts argument */ 63 /* Delete the extra opts argument */
59 for (j = i; j < *argc; j++) 64 for (int j = i; j < *argc; j++) {
60 argv[j] = argv[j + 1]; 65 argv[j] = argv[j + 1];
66 }
67
61 i--; 68 i--;
62 *argc -= 1; 69 *argc -= 1;
63 } else if (strcmp(argv[i], "--extra-opts") == 0) { 70 } else if (strcmp(argv[i], "--extra-opts") == 0) {
@@ -65,8 +72,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) {
65 /* It is a argument with separate value */ 72 /* It is a argument with separate value */
66 argptr = argv[i + 1]; 73 argptr = argv[i + 1];
67 /* Delete the extra-opts argument/value */ 74 /* Delete the extra-opts argument/value */
68 for (j = i; j < *argc - 1; j++) 75 for (int j = i; j < *argc - 1; j++) {
69 argv[j] = argv[j + 2]; 76 argv[j] = argv[j + 2];
77 }
78
70 i -= 2; 79 i -= 2;
71 *argc -= 2; 80 *argc -= 2;
72 ea_num--; 81 ea_num--;
@@ -74,8 +83,10 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) {
74 /* It has no value */ 83 /* It has no value */
75 optfound = 1; 84 optfound = 1;
76 /* Delete the extra opts argument */ 85 /* Delete the extra opts argument */
77 for (j = i; j < *argc; j++) 86 for (int j = i; j < *argc; j++) {
78 argv[j] = argv[j + 1]; 87 argv[j] = argv[j + 1];
88 }
89
79 i--; 90 i--;
80 *argc -= 1; 91 *argc -= 1;
81 } 92 }
@@ -94,34 +105,37 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) {
94 /* append the list to extra_args */ 105 /* append the list to extra_args */
95 if (extra_args == NULL) { 106 if (extra_args == NULL) {
96 extra_args = ea1; 107 extra_args = ea1;
97 while ((ea1 = ea1->next)) 108 while ((ea1 = ea1->next)) {
98 ea_num++; 109 ea_num++;
110 }
99 } else { 111 } else {
100 ea_tmp = extra_args; 112 ea_tmp = extra_args;
101 while (ea_tmp->next) { 113 while (ea_tmp->next) {
102 ea_tmp = ea_tmp->next; 114 ea_tmp = ea_tmp->next;
103 } 115 }
104 ea_tmp->next = ea1; 116 ea_tmp->next = ea1;
105 while ((ea1 = ea1->next)) 117 while ((ea1 = ea1->next)) {
106 ea_num++; 118 ea_num++;
119 }
107 } 120 }
108 ea1 = ea_tmp = NULL; 121 ea1 = ea_tmp = NULL;
109 } 122 }
110 } /* lather, rince, repeat */ 123 } /* lather, rince, repeat */
111 124
112 if (ea_num == *argc && extra_args == NULL) { 125 if (ea_num == (size_t)*argc && extra_args == NULL) {
113 /* No extra-opts */ 126 /* No extra-opts */
114 return argv; 127 return argv;
115 } 128 }
116 129
117 /* done processing arguments. now create a new argv array... */ 130 /* done processing arguments. now create a new argv array... */
118 argv_new = (char **)malloc((ea_num + 1) * sizeof(char **)); 131 char **argv_new = (char **)malloc((ea_num + 1) * sizeof(char **));
119 if (argv_new == NULL) 132 if (argv_new == NULL) {
120 die(STATE_UNKNOWN, _("malloc() failed!\n")); 133 die(STATE_UNKNOWN, _("malloc() failed!\n"));
134 }
121 135
122 /* starting with program name */ 136 /* starting with program name */
123 argv_new[0] = argv[0]; 137 argv_new[0] = argv[0];
124 argc_new = 1; 138 int argc_new = 1;
125 /* then parsed ini opts (frying them up in the same run) */ 139 /* then parsed ini opts (frying them up in the same run) */
126 while (extra_args) { 140 while (extra_args) {
127 argv_new[argc_new++] = extra_args->arg; 141 argv_new[argc_new++] = extra_args->arg;
@@ -130,8 +144,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) {
130 free(ea1); 144 free(ea1);
131 } 145 }
132 /* finally the rest of the argv array */ 146 /* finally the rest of the argv array */
133 for (i = 1; i < *argc; i++) 147 for (int i = 1; i < *argc; i++) {
134 argv_new[argc_new++] = argv[i]; 148 argv_new[argc_new++] = argv[i];
149 }
135 *argc = argc_new; 150 *argc = argc_new;
136 /* and terminate. */ 151 /* and terminate. */
137 argv_new[argc_new] = NULL; 152 argv_new[argc_new] = NULL;
diff --git a/lib/maxfd.c b/lib/maxfd.c
index ca5b6e54..a0f79949 100644
--- a/lib/maxfd.c
+++ b/lib/maxfd.c
@@ -19,7 +19,6 @@
19 *****************************************************************************/ 19 *****************************************************************************/
20 20
21#include "./maxfd.h" 21#include "./maxfd.h"
22#include <errno.h>
23 22
24long mp_open_max(void) { 23long mp_open_max(void) {
25 long maxfd = 0L; 24 long maxfd = 0L;
@@ -31,10 +30,11 @@ long mp_open_max(void) {
31#ifdef _SC_OPEN_MAX 30#ifdef _SC_OPEN_MAX
32 errno = 0; 31 errno = 0;
33 if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) { 32 if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) {
34 if (errno == 0) 33 if (errno == 0) {
35 maxfd = DEFAULT_MAXFD; /* it's indeterminate */ 34 maxfd = DEFAULT_MAXFD; /* it's indeterminate */
36 else 35 } else {
37 die(STATE_UNKNOWN, _("sysconf error for _SC_OPEN_MAX\n")); 36 die(STATE_UNKNOWN, _("sysconf error for _SC_OPEN_MAX\n"));
37 }
38 } 38 }
39#elif defined(OPEN_MAX) 39#elif defined(OPEN_MAX)
40 return OPEN_MAX 40 return OPEN_MAX
diff --git a/lib/monitoringplug.h b/lib/monitoringplug.h
new file mode 100644
index 00000000..a555d736
--- /dev/null
+++ b/lib/monitoringplug.h
@@ -0,0 +1,7 @@
1#pragma once
2
3#include "./states.h"
4#include "./utils_base.h"
5#include "./thresholds.h"
6#include "./maxfd.h"
7#include "./output.h"
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 00000000..f283969f
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,636 @@
1#include "./output.h"
2#include "./utils_base.h"
3#include "../plugins/utils.h"
4
5#include <assert.h>
6#include <stdlib.h>
7#include <string.h>
8#include <strings.h>
9// #include <cjson/cJSON.h>
10#include "./vendor/cJSON/cJSON.h"
11#include "perfdata.h"
12#include "states.h"
13
14// == Global variables
15static mp_output_format output_format = MP_FORMAT_DEFAULT;
16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL;
17
18// == Prototypes ==
19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
20 unsigned int indentation);
21static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
22
23// == Implementation ==
24
25/*
26 * Generate output string for a mp_subcheck object
27 */
28static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
29 char *result = strdup("");
30 int added = 0;
31
32 if (check.perfdata != NULL) {
33 added = asprintf(&result, "%s", pd_list_to_string(*check.perfdata));
34 }
35
36 if (check.subchecks == NULL) {
37 // No subchecks, return here
38 return result;
39 }
40
41 mp_subcheck_list *subchecks = check.subchecks;
42
43 while (subchecks != NULL) {
44 if (added > 0) {
45 added = asprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
46 } else {
47 // TODO free previous result here?
48 added = asprintf(&result, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
49 }
50
51 subchecks = subchecks->next;
52 }
53
54 return result;
55}
56
57/*
58 * Initialiser for a mp_check object. Always use this to get a new one!
59 * It sets useful defaults
60 */
61mp_check mp_check_init(void) {
62 mp_check check = {
63 .evaluation_function = &mp_eval_check_default,
64 };
65 return check;
66}
67
68/*
69 * Initialiser for a mp_subcheck object. Always use this to get a new one!
70 * It sets useful defaults
71 */
72mp_subcheck mp_subcheck_init(void) {
73 mp_subcheck tmp = {0};
74 tmp.default_state = STATE_UNKNOWN; // Default state is unknown
75 tmp.state_set_explicitly = false;
76 return tmp;
77}
78
79/*
80 * Add a subcheck to a (the one and only) check object
81 */
82int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) {
83 assert(subcheck.output != NULL); // There must be output in a subcheck
84
85 mp_subcheck_list *tmp = NULL;
86
87 if (check->subchecks == NULL) {
88 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
89 if (check->subchecks == NULL) {
90 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
91 }
92
93 check->subchecks->subcheck = subcheck;
94 check->subchecks->next = NULL;
95 } else {
96 // Search for the end
97 tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
98 if (tmp == NULL) {
99 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
100 }
101
102 tmp->subcheck = subcheck;
103 tmp->next = check->subchecks;
104
105 check->subchecks = tmp;
106 }
107
108 return 0;
109}
110
111/*
112 * Add a mp_perfdata data point to a mp_subcheck object
113 */
114void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) {
115 if (check->perfdata == NULL) {
116 check->perfdata = pd_list_init();
117 }
118 pd_list_append(check->perfdata, perfData);
119}
120
121/*
122 * Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the
123 * hierarchy
124 */
125int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
126 if (subcheck.output == NULL) {
127 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__,
128 "Sub check output is NULL");
129 }
130
131 mp_subcheck_list *tmp = NULL;
132
133 if (check->subchecks == NULL) {
134 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
135 if (check->subchecks == NULL) {
136 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
137 }
138
139 tmp = check->subchecks;
140 } else {
141 // Search for the end
142 tmp = check->subchecks;
143
144 while (tmp->next != NULL) {
145 tmp = tmp->next;
146 }
147
148 tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
149 if (tmp->next == NULL) {
150 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
151 }
152
153 tmp = tmp->next;
154 }
155
156 tmp->subcheck = subcheck;
157
158 return 0;
159}
160
161/*
162 * Add a manual summary to a mp_check object, effectively replacing
163 * the autogenerated one
164 */
165void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; }
166
167/*
168 * Generate the summary string of a mp_check object based on it's subchecks
169 */
170char *get_subcheck_summary(mp_check check) {
171 mp_subcheck_list *subchecks = check.subchecks;
172
173 unsigned int ok = 0;
174 unsigned int warning = 0;
175 unsigned int critical = 0;
176 unsigned int unknown = 0;
177 while (subchecks != NULL) {
178 switch (subchecks->subcheck.state) {
179 case STATE_OK:
180 ok++;
181 break;
182 case STATE_WARNING:
183 warning++;
184 break;
185 case STATE_CRITICAL:
186 critical++;
187 break;
188 case STATE_UNKNOWN:
189 unknown++;
190 break;
191 default:
192 die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
193 }
194 subchecks = subchecks->next;
195 }
196 char *result = NULL;
197 asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown);
198 return result;
199}
200
201mp_state_enum mp_compute_subcheck_state(const mp_subcheck subcheck) {
202 if (subcheck.evaluation_function == NULL) {
203 return mp_eval_subcheck_default(subcheck);
204 }
205 return subcheck.evaluation_function(subcheck);
206}
207
208/*
209 * Generate the result state of a mp_subcheck object based on its own state and its subchecks
210 * states
211 */
212mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) {
213 if (subcheck.evaluation_function != NULL) {
214 return subcheck.evaluation_function(subcheck);
215 }
216
217 if (subcheck.state_set_explicitly) {
218 return subcheck.state;
219 }
220
221 mp_subcheck_list *scl = subcheck.subchecks;
222
223 if (scl == NULL) {
224 return subcheck.default_state;
225 }
226
227 mp_state_enum result = STATE_OK;
228
229 while (scl != NULL) {
230 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
231 scl = scl->next;
232 }
233
234 return result;
235}
236
237mp_state_enum mp_compute_check_state(const mp_check check) {
238 // just a safety check
239 if (check.evaluation_function == NULL) {
240 return mp_eval_check_default(check);
241 }
242 return check.evaluation_function(check);
243}
244
245/*
246 * Generate the result state of a mp_check object based on it's own state and it's subchecks states
247 */
248mp_state_enum mp_eval_check_default(const mp_check check) {
249 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
250
251 mp_subcheck_list *scl = check.subchecks;
252 mp_state_enum result = STATE_OK;
253
254 while (scl != NULL) {
255 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
256 scl = scl->next;
257 }
258
259 return result;
260}
261
262/*
263 * Generate output string for a mp_check object
264 * Non static to be available for testing functions
265 */
266char *mp_fmt_output(mp_check check) {
267 char *result = NULL;
268
269 switch (output_format) {
270 case MP_FORMAT_MULTI_LINE: {
271 if (check.summary == NULL) {
272 check.summary = get_subcheck_summary(check);
273 }
274
275 asprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary);
276
277 mp_subcheck_list *subchecks = check.subchecks;
278
279 while (subchecks != NULL) {
280 if (level_of_detail == MP_DETAIL_ALL ||
281 mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) {
282 asprintf(&result, "%s\n%s", result,
283 fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1));
284 }
285 subchecks = subchecks->next;
286 }
287
288 char *pd_string = NULL;
289 subchecks = check.subchecks;
290
291 while (subchecks != NULL) {
292 if (pd_string == NULL) {
293 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
294 } else {
295 asprintf(&pd_string, "%s %s", pd_string,
296 fmt_subcheck_perfdata(subchecks->subcheck));
297 }
298
299 subchecks = subchecks->next;
300 }
301
302 if (pd_string != NULL && strlen(pd_string) > 0) {
303 asprintf(&result, "%s|%s", result, pd_string);
304 }
305
306 break;
307 }
308 case MP_FORMAT_TEST_JSON: {
309 cJSON *resultObject = cJSON_CreateObject();
310 if (resultObject == NULL) {
311 die(STATE_UNKNOWN, "cJSON_CreateObject failed");
312 }
313
314 cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check)));
315 cJSON_AddItemToObject(resultObject, "state", resultState);
316
317 if (check.summary == NULL) {
318 check.summary = get_subcheck_summary(check);
319 }
320
321 cJSON *summary = cJSON_CreateString(check.summary);
322 cJSON_AddItemToObject(resultObject, "summary", summary);
323
324 if (check.subchecks != NULL) {
325 cJSON *subchecks = cJSON_CreateArray();
326
327 mp_subcheck_list *sc = check.subchecks;
328
329 while (sc != NULL) {
330 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
331 cJSON_AddItemToArray(subchecks, sc_json);
332 sc = sc->next;
333 }
334
335 cJSON_AddItemToObject(resultObject, "checks", subchecks);
336 }
337
338 result = cJSON_PrintUnformatted(resultObject);
339 break;
340 }
341 default:
342 die(STATE_UNKNOWN, "Invalid format");
343 }
344
345 return result;
346}
347
348/*
349 * Helper function to properly indent the output lines when using multiline
350 * formats
351 */
352static char *generate_indentation_string(unsigned int indentation) {
353 char *result = calloc(indentation + 1, sizeof(char));
354
355 for (unsigned int i = 0; i < indentation; i++) {
356 result[i] = '\t';
357 }
358
359 return result;
360}
361
362/*
363 * Helper function to generate the output string of mp_subcheck
364 */
365static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
366 unsigned int indentation) {
367 char *result = NULL;
368 mp_subcheck_list *subchecks = NULL;
369
370 switch (output_format) {
371 case MP_FORMAT_MULTI_LINE: {
372 char *tmp_string = NULL;
373 if ((tmp_string = strchr(check.output, '\n')) != NULL) {
374 // This is a multiline string, put the correct indentation in before proceeding
375 char *intermediate_string = "";
376 bool have_residual_chars = false;
377
378 while (tmp_string != NULL) {
379 *tmp_string = '\0';
380 asprintf(&intermediate_string, "%s%s\n%s", intermediate_string, check.output,
381 generate_indentation_string(
382 indentation + 1)); // one more indentation to make it look better
383
384 if (*(tmp_string + 1) != '\0') {
385 check.output = tmp_string + 1;
386 have_residual_chars = true;
387 } else {
388 // Null after the \n, so this is the end
389 have_residual_chars = false;
390 break;
391 }
392
393 tmp_string = strchr(check.output, '\n');
394 }
395
396 // add the rest (if any)
397 if (have_residual_chars) {
398 char *tmp = check.output;
399 xasprintf(&check.output, "%s\n%s%s", intermediate_string,
400 generate_indentation_string(indentation + 1), tmp);
401 } else {
402 check.output = intermediate_string;
403 }
404 }
405 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation),
406 state_text(mp_compute_subcheck_state(check)), check.output);
407
408 subchecks = check.subchecks;
409
410 while (subchecks != NULL) {
411 asprintf(&result, "%s\n%s", result,
412 fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
413 subchecks = subchecks->next;
414 }
415 return result;
416 }
417 default:
418 die(STATE_UNKNOWN, "Invalid format");
419 }
420}
421
422static inline cJSON *json_serialise_pd_value(mp_perfdata_value value) {
423 cJSON *result = cJSON_CreateObject();
424
425 switch (value.type) {
426 case PD_TYPE_DOUBLE:
427 cJSON_AddStringToObject(result, "type", "double");
428 break;
429 case PD_TYPE_INT:
430 cJSON_AddStringToObject(result, "type", "int");
431 break;
432 case PD_TYPE_UINT:
433 cJSON_AddStringToObject(result, "type", "uint");
434 break;
435 case PD_TYPE_NONE:
436 die(STATE_UNKNOWN, "Perfdata type was None in json_serialise_pd_value");
437 }
438 cJSON_AddStringToObject(result, "value", pd_value_to_string(value));
439
440 return result;
441}
442
443static inline cJSON *json_serialise_range(mp_range range) {
444 cJSON *result = cJSON_CreateObject();
445
446 if (range.alert_on_inside_range) {
447 cJSON_AddBoolToObject(result, "alert_on_inside", true);
448 } else {
449 cJSON_AddBoolToObject(result, "alert_on_inside", false);
450 }
451
452 if (range.end_infinity) {
453 cJSON_AddStringToObject(result, "end", "inf");
454 } else {
455 cJSON_AddItemToObject(result, "end", json_serialise_pd_value(range.end));
456 }
457
458 if (range.start_infinity) {
459 cJSON_AddStringToObject(result, "start", "inf");
460 } else {
461 cJSON_AddItemToObject(result, "start", json_serialise_pd_value(range.end));
462 }
463
464 return result;
465}
466
467static inline cJSON *json_serialise_pd(mp_perfdata pd_val) {
468 cJSON *result = cJSON_CreateObject();
469
470 // Label
471 cJSON_AddStringToObject(result, "label", pd_val.label);
472
473 // Value
474 cJSON_AddItemToObject(result, "value", json_serialise_pd_value(pd_val.value));
475
476 // Uom
477 cJSON_AddStringToObject(result, "uom", pd_val.uom);
478
479 // Warn/Crit
480 if (pd_val.warn_present) {
481 cJSON *warn = json_serialise_range(pd_val.warn);
482 cJSON_AddItemToObject(result, "warn", warn);
483 }
484 if (pd_val.crit_present) {
485 cJSON *crit = json_serialise_range(pd_val.crit);
486 cJSON_AddItemToObject(result, "crit", crit);
487 }
488
489 if (pd_val.min_present) {
490 cJSON_AddItemToObject(result, "min", json_serialise_pd_value(pd_val.min));
491 }
492 if (pd_val.max_present) {
493 cJSON_AddItemToObject(result, "max", json_serialise_pd_value(pd_val.max));
494 }
495
496 return result;
497}
498
499static inline cJSON *json_serialise_pd_list(pd_list *list) {
500 cJSON *result = cJSON_CreateArray();
501
502 do {
503 cJSON *pd_value = json_serialise_pd(list->data);
504 cJSON_AddItemToArray(result, pd_value);
505 list = list->next;
506 } while (list != NULL);
507
508 return result;
509}
510
511static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) {
512 cJSON *result = cJSON_CreateObject();
513
514 // Human readable output
515 cJSON *output = cJSON_CreateString(subcheck.output);
516 cJSON_AddItemToObject(result, "output", output);
517
518 // Test state (aka Exit Code)
519 cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck)));
520 cJSON_AddItemToObject(result, "state", state);
521
522 // Perfdata
523 if (subcheck.perfdata != NULL) {
524 cJSON *perfdata = json_serialise_pd_list(subcheck.perfdata);
525 cJSON_AddItemToObject(result, "perfdata", perfdata);
526 }
527
528 if (subcheck.subchecks != NULL) {
529 cJSON *subchecks = cJSON_CreateArray();
530
531 mp_subcheck_list *sc = subcheck.subchecks;
532
533 while (sc != NULL) {
534 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
535 cJSON_AddItemToArray(subchecks, sc_json);
536 sc = sc->next;
537 }
538
539 cJSON_AddItemToObject(result, "checks", subchecks);
540 }
541
542 return result;
543}
544
545/*
546 * Wrapper function to print the output string of a mp_check object
547 * Use this in concrete plugins.
548 */
549void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); }
550
551/*
552 * Convenience function to print the output string of a mp_check object and exit
553 * the program with the resulting state.
554 * Intended to be used to exit a monitoring plugin.
555 */
556void mp_exit(mp_check check) {
557 mp_print_output(check);
558 if (output_format == MP_FORMAT_TEST_JSON) {
559 exit(0);
560 }
561
562 exit(mp_compute_check_state(check));
563}
564
565/*
566 * Function to set the result state of a mp_subcheck object explicitly.
567 * This will overwrite the default state AND states derived from it's subchecks
568 */
569mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) {
570 check.state = state;
571 check.state_set_explicitly = true;
572 return check;
573}
574
575/*
576 * Function to set the default result state of a mp_subcheck object. This state
577 * will be used if neither an explicit state is set (see *mp_set_subcheck_state*)
578 * nor does it include other subchecks
579 */
580mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) {
581 check.default_state = state;
582 return check;
583}
584
585char *mp_output_format_map[] = {
586 [MP_FORMAT_MULTI_LINE] = "multi-line",
587 [MP_FORMAT_TEST_JSON] = "mp-test-json",
588};
589
590/*
591 * Function to parse the output from a string
592 */
593parsed_output_format mp_parse_output_format(char *format_string) {
594 parsed_output_format result = {
595 .parsing_success = false,
596 .output_format = MP_FORMAT_DEFAULT,
597 };
598
599 for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) {
600 if (strcasecmp(mp_output_format_map[i], format_string) == 0) {
601 result.parsing_success = true;
602 result.output_format = i;
603 break;
604 }
605 }
606
607 return result;
608}
609
610void mp_set_format(mp_output_format format) { output_format = format; }
611
612mp_output_format mp_get_format(void) { return output_format; }
613
614void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; }
615
616mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; }
617
618mp_state_enum mp_eval_ok(mp_check overall) {
619 (void)overall;
620 return STATE_OK;
621}
622
623mp_state_enum mp_eval_warning(mp_check overall) {
624 (void)overall;
625 return STATE_WARNING;
626}
627
628mp_state_enum mp_eval_critical(mp_check overall) {
629 (void)overall;
630 return STATE_CRITICAL;
631}
632
633mp_state_enum mp_eval_unknown(mp_check overall) {
634 (void)overall;
635 return STATE_UNKNOWN;
636}
diff --git a/lib/output.h b/lib/output.h
new file mode 100644
index 00000000..c63c8e3f
--- /dev/null
+++ b/lib/output.h
@@ -0,0 +1,117 @@
1#pragma once
2
3#include "../config.h"
4#include "./perfdata.h"
5#include "./states.h"
6
7/*
8 * A partial check result
9 */
10typedef struct mp_subcheck mp_subcheck;
11struct mp_subcheck {
12 mp_state_enum state; // OK, Warning, Critical ... set explicitly
13 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
14 bool state_set_explicitly; // was the state set explicitly (or should it be derived from
15 // subchecks)
16
17 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP
18 // connection to..")
19 pd_list *perfdata; // Performance data for this check
20 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
21
22 // the evaluation_functions computes the state of subcheck
23 mp_state_enum (*evaluation_function)(mp_subcheck);
24};
25
26/*
27 * A list of subchecks, used in subchecks and the main check
28 */
29typedef struct subcheck_list {
30 mp_subcheck subcheck;
31 struct subcheck_list *next;
32} mp_subcheck_list;
33
34/*
35 * Possible output formats
36 */
37typedef enum output_format {
38 MP_FORMAT_MULTI_LINE,
39 MP_FORMAT_TEST_JSON,
40} mp_output_format;
41
42#define MP_FORMAT_DEFAULT MP_FORMAT_MULTI_LINE
43
44/*
45 * Format related functions
46 */
47void mp_set_format(mp_output_format format);
48mp_output_format mp_get_format(void);
49
50// Output detail level
51
52typedef enum output_detail_level {
53 MP_DETAIL_ALL,
54 MP_DETAIL_NON_OK_ONLY,
55} mp_output_detail_level;
56
57void mp_set_level_of_detail(mp_output_detail_level level);
58mp_output_detail_level mp_get_level_of_detail(void);
59
60/*
61 * The main state object of a plugin. Exists only ONCE per plugin.
62 * This is the "root" of a tree of singular checks.
63 * The final result is always derived from the children and the "worst" state
64 * in the first layer of subchecks
65 */
66typedef struct mp_check mp_check;
67struct mp_check {
68 char *summary; // Overall summary, if not set a summary will be automatically generated
69 mp_subcheck_list *subchecks;
70
71 // the evaluation_functions computes the state of check
72 mp_state_enum (*evaluation_function)(mp_check);
73};
74
75mp_check mp_check_init(void);
76mp_subcheck mp_subcheck_init(void);
77
78mp_subcheck mp_set_subcheck_state(mp_subcheck, mp_state_enum);
79mp_subcheck mp_set_subcheck_default_state(mp_subcheck, mp_state_enum);
80
81int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck);
82int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck);
83
84void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], mp_perfdata);
85
86void mp_add_summary(mp_check check[static 1], char *summary);
87
88mp_state_enum mp_compute_check_state(mp_check);
89mp_state_enum mp_compute_subcheck_state(mp_subcheck);
90
91mp_state_enum mp_eval_ok(mp_check overall);
92mp_state_enum mp_eval_warning(mp_check overall);
93mp_state_enum mp_eval_critical(mp_check overall);
94mp_state_enum mp_eval_unknown(mp_check overall);
95mp_state_enum mp_eval_check_default(mp_check check);
96mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck);
97
98typedef struct {
99 bool parsing_success;
100 mp_output_format output_format;
101} parsed_output_format;
102parsed_output_format mp_parse_output_format(char *format_string);
103
104// TODO free and stuff
105// void mp_cleanup_check(mp_check check[static 1]);
106
107char *mp_fmt_output(mp_check);
108
109void mp_print_output(mp_check);
110
111/*
112 * ==================
113 * Exit functionality
114 * ==================
115 */
116
117void mp_exit(mp_check) __attribute__((noreturn));
diff --git a/lib/parse_ini.c b/lib/parse_ini.c
index 1289aae2..db337622 100644
--- a/lib/parse_ini.c
+++ b/lib/parse_ini.c
@@ -40,26 +40,29 @@ typedef struct {
40 char *stanza; 40 char *stanza;
41} np_ini_info; 41} np_ini_info;
42 42
43static char *default_ini_file_names[] = {"monitoring-plugins.ini", "plugins.ini", "nagios-plugins.ini", NULL}; 43static char *default_ini_file_names[] = {"monitoring-plugins.ini", "plugins.ini",
44 "nagios-plugins.ini", NULL};
44 45
45static char *default_ini_path_names[] = { 46static char *default_ini_path_names[] = {
46 "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini", "/usr/local/etc/monitoring-plugins.ini", 47 "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini",
47 "/etc/monitoring-plugins/monitoring-plugins.ini", "/etc/monitoring-plugins.ini", 48 "/usr/local/etc/monitoring-plugins.ini", "/etc/monitoring-plugins/monitoring-plugins.ini",
49 "/etc/monitoring-plugins.ini",
48 /* deprecated path names (for backward compatibility): */ 50 /* deprecated path names (for backward compatibility): */
49 "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini", "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", 51 "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini",
50 "/etc/nagios-plugins.ini", "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", NULL}; 52 "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", "/etc/nagios-plugins.ini",
53 "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", NULL};
51 54
52/* eat all characters from a FILE pointer until n is encountered */ 55/* eat all characters from a FILE pointer until n is encountered */
53#define GOBBLE_TO(f, c, n) \ 56#define GOBBLE_TO(f, c, n) \
54 do { \ 57 do { \
55 (c) = fgetc((f)); \ 58 (c) = fgetc((f)); \
56 } while ((c) != EOF && (c) != (n)) 59 } while ((c) != EOF && (c) != (n))
57 60
58/* internal function that returns the constructed defaults options */ 61/* internal function that returns the constructed defaults options */
59static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts); 62static bool read_defaults(FILE *defaults_file, const char *stanza, np_arg_list **opts);
60 63
61/* internal function that converts a single line into options format */ 64/* internal function that converts a single line into options format */
62static int add_option(FILE *f, np_arg_list **optlst); 65static int add_option(FILE *filePointer, np_arg_list **optlst);
63 66
64/* internal functions to find default file */ 67/* internal functions to find default file */
65static char *default_file(void); 68static char *default_file(void);
@@ -71,7 +74,8 @@ static char *default_file_in_path(void);
71 * into its separate parts. 74 * into its separate parts.
72 */ 75 */
73static void parse_locator(const char *locator, const char *def_stanza, np_ini_info *i) { 76static void parse_locator(const char *locator, const char *def_stanza, np_ini_info *i) {
74 size_t locator_len = 0, stanza_len = 0; 77 size_t locator_len = 0;
78 size_t stanza_len = 0;
75 79
76 /* if locator is NULL we'll use default values */ 80 /* if locator is NULL we'll use default values */
77 if (locator != NULL) { 81 if (locator != NULL) {
@@ -87,8 +91,9 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in
87 i->stanza = strdup(def_stanza); 91 i->stanza = strdup(def_stanza);
88 } 92 }
89 93
90 if (i->stanza == NULL) 94 if (i->stanza == NULL) {
91 die(STATE_UNKNOWN, _("malloc() failed!\n")); 95 die(STATE_UNKNOWN, _("malloc() failed!\n"));
96 }
92 97
93 /* check whether there's an @file part */ 98 /* check whether there's an @file part */
94 if (stanza_len == locator_len) { 99 if (stanza_len == locator_len) {
@@ -99,39 +104,46 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in
99 i->file_string_on_heap = true; 104 i->file_string_on_heap = true;
100 } 105 }
101 106
102 if (i->file == NULL || i->file[0] == '\0') 107 if (i->file == NULL || i->file[0] == '\0') {
103 die(STATE_UNKNOWN, _("Cannot find config file in any standard location.\n")); 108 die(STATE_UNKNOWN, _("Cannot find config file in any standard location.\n"));
109 }
104} 110}
105 111
106/* 112/*
107 * This is the externally visible function used by extra_opts. 113 * This is the externally visible function used by extra_opts.
108 */ 114 */
109np_arg_list *np_get_defaults(const char *locator, const char *default_section) { 115np_arg_list *np_get_defaults(const char *locator, const char *default_section) {
110 FILE *inifile = NULL;
111 np_arg_list *defaults = NULL;
112 np_ini_info i;
113 int is_suid_plugin = mp_suid(); 116 int is_suid_plugin = mp_suid();
114 117
115 if (is_suid_plugin && idpriv_temp_drop() == -1) 118 if (is_suid_plugin && idpriv_temp_drop() == -1) {
116 die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"), strerror(errno)); 119 die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"), strerror(errno));
120 }
117 121
118 parse_locator(locator, default_section, &i); 122 FILE *inifile = NULL;
119 inifile = strcmp(i.file, "-") == 0 ? stdin : fopen(i.file, "r"); 123 np_ini_info ini_info;
124 parse_locator(locator, default_section, &ini_info);
125 inifile = strcmp(ini_info.file, "-") == 0 ? stdin : fopen(ini_info.file, "r");
120 126
121 if (inifile == NULL) 127 if (inifile == NULL) {
122 die(STATE_UNKNOWN, _("Can't read config file: %s\n"), strerror(errno)); 128 die(STATE_UNKNOWN, _("Can't read config file: %s\n"), strerror(errno));
123 if (!read_defaults(inifile, i.stanza, &defaults)) 129 }
124 die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), i.stanza, i.file);
125 130
126 if (i.file_string_on_heap) { 131 np_arg_list *defaults = NULL;
127 free(i.file); 132 if (!read_defaults(inifile, ini_info.stanza, &defaults)) {
133 die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), ini_info.stanza, ini_info.file);
134 }
135
136 if (ini_info.file_string_on_heap) {
137 free(ini_info.file);
128 } 138 }
129 139
130 if (inifile != stdin) 140 if (inifile != stdin) {
131 fclose(inifile); 141 fclose(inifile);
132 free(i.stanza); 142 }
133 if (is_suid_plugin && idpriv_temp_restore() == -1) 143 free(ini_info.stanza);
144 if (is_suid_plugin && idpriv_temp_restore() == -1) {
134 die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"), strerror(errno)); 145 die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"), strerror(errno));
146 }
135 147
136 return defaults; 148 return defaults;
137} 149}
@@ -143,54 +155,58 @@ np_arg_list *np_get_defaults(const char *locator, const char *default_section) {
143 * be extra careful about user-supplied input (i.e. avoiding possible 155 * be extra careful about user-supplied input (i.e. avoiding possible
144 * format string vulnerabilities, etc). 156 * format string vulnerabilities, etc).
145 */ 157 */
146static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { 158static bool read_defaults(FILE *defaults_file, const char *stanza, np_arg_list **opts) {
147 int c = 0;
148 bool status = false; 159 bool status = false;
149 size_t i, stanza_len;
150 enum { 160 enum {
151 NOSTANZA, 161 NOSTANZA,
152 WRONGSTANZA, 162 WRONGSTANZA,
153 RIGHTSTANZA 163 RIGHTSTANZA
154 } stanzastate = NOSTANZA; 164 } stanzastate = NOSTANZA;
155 165
156 stanza_len = strlen(stanza); 166 size_t stanza_len = strlen(stanza);
157 167
158 /* our little stanza-parsing state machine */ 168 /* our little stanza-parsing state machine */
159 while ((c = fgetc(f)) != EOF) { 169 int current_char = 0;
170 while ((current_char = fgetc(defaults_file)) != EOF) {
160 /* gobble up leading whitespace */ 171 /* gobble up leading whitespace */
161 if (isspace(c)) 172 if (isspace(current_char)) {
162 continue; 173 continue;
163 switch (c) { 174 }
175 switch (current_char) {
164 /* globble up comment lines */ 176 /* globble up comment lines */
165 case ';': 177 case ';':
166 case '#': 178 case '#':
167 GOBBLE_TO(f, c, '\n'); 179 GOBBLE_TO(defaults_file, current_char, '\n');
168 break; 180 break;
169 /* start of a stanza, check to see if it matches */ 181 /* start of a stanza, check to see if it matches */
170 case '[': 182 case '[': {
171 stanzastate = WRONGSTANZA; 183 stanzastate = WRONGSTANZA;
184 size_t i;
172 for (i = 0; i < stanza_len; i++) { 185 for (i = 0; i < stanza_len; i++) {
173 c = fgetc(f); 186 current_char = fgetc(defaults_file);
174 /* strip leading whitespace */ 187 /* strip leading whitespace */
175 if (i == 0) 188 if (i == 0) {
176 for (; isspace(c); c = fgetc(f)) 189 for (; isspace(current_char); current_char = fgetc(defaults_file)) {
177 continue; 190 }
191 }
178 /* nope, read to the end of the line */ 192 /* nope, read to the end of the line */
179 if (c != stanza[i]) { 193 if (current_char != stanza[i]) {
180 GOBBLE_TO(f, c, '\n'); 194 GOBBLE_TO(defaults_file, current_char, '\n');
181 break; 195 break;
182 } 196 }
183 } 197 }
198
184 /* if it matched up to here and the next char is ']'... */ 199 /* if it matched up to here and the next char is ']'... */
185 if (i == stanza_len) { 200 if (i == stanza_len) {
186 c = fgetc(f); 201 current_char = fgetc(defaults_file);
187 /* strip trailing whitespace */ 202 /* strip trailing whitespace */
188 for (; isspace(c); c = fgetc(f)) 203 for (; isspace(current_char); current_char = fgetc(defaults_file)) {
189 continue; 204 }
190 if (c == ']') 205 if (current_char == ']') {
191 stanzastate = RIGHTSTANZA; 206 stanzastate = RIGHTSTANZA;
207 }
192 } 208 }
193 break; 209 } break;
194 /* otherwise, we're in the body of a stanza or a parse error */ 210 /* otherwise, we're in the body of a stanza or a parse error */
195 default: 211 default:
196 switch (stanzastate) { 212 switch (stanzastate) {
@@ -201,12 +217,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) {
201 die(STATE_UNKNOWN, "%s\n", _("Config file error")); 217 die(STATE_UNKNOWN, "%s\n", _("Config file error"));
202 /* we're in a stanza, but for a different plugin */ 218 /* we're in a stanza, but for a different plugin */
203 case WRONGSTANZA: 219 case WRONGSTANZA:
204 GOBBLE_TO(f, c, '\n'); 220 GOBBLE_TO(defaults_file, current_char, '\n');
205 break; 221 break;
206 /* okay, this is where we start taking the config */ 222 /* okay, this is where we start taking the config */
207 case RIGHTSTANZA: 223 case RIGHTSTANZA:
208 ungetc(c, f); 224 ungetc(current_char, defaults_file);
209 if (add_option(f, opts)) { 225 if (add_option(defaults_file, opts)) {
210 die(STATE_UNKNOWN, "%s\n", _("Config file error")); 226 die(STATE_UNKNOWN, "%s\n", _("Config file error"));
211 } 227 }
212 status = true; 228 status = true;
@@ -225,13 +241,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) {
225 * --option[=value] 241 * --option[=value]
226 * appending it to the linked list optbuf. 242 * appending it to the linked list optbuf.
227 */ 243 */
228static int add_option(FILE *f, np_arg_list **optlst) { 244static int add_option(FILE *filePointer, np_arg_list **optlst) {
229 np_arg_list *opttmp = *optlst, *optnew; 245 char *linebuf = NULL;
230 char *linebuf = NULL, *lineend = NULL, *optptr = NULL, *optend = NULL; 246 bool done_reading = false;
231 char *eqptr = NULL, *valptr = NULL, *valend = NULL; 247 const size_t read_sz = 8;
232 short done_reading = 0, equals = 0, value = 0; 248 size_t linebuf_sz = 0;
233 size_t cfg_len = 0, read_sz = 8, linebuf_sz = 0, read_pos = 0; 249 size_t read_pos = 0;
234 size_t opt_len = 0, val_len = 0;
235 250
236 /* read one line from the file */ 251 /* read one line from the file */
237 while (!done_reading) { 252 while (!done_reading) {
@@ -239,74 +254,101 @@ static int add_option(FILE *f, np_arg_list **optlst) {
239 if (linebuf == NULL || read_pos + read_sz >= linebuf_sz) { 254 if (linebuf == NULL || read_pos + read_sz >= linebuf_sz) {
240 linebuf_sz = linebuf_sz > 0 ? linebuf_sz << 1 : read_sz; 255 linebuf_sz = linebuf_sz > 0 ? linebuf_sz << 1 : read_sz;
241 linebuf = realloc(linebuf, linebuf_sz); 256 linebuf = realloc(linebuf, linebuf_sz);
242 if (linebuf == NULL) 257 if (linebuf == NULL) {
243 die(STATE_UNKNOWN, _("malloc() failed!\n")); 258 die(STATE_UNKNOWN, _("malloc() failed!\n"));
259 }
244 } 260 }
245 if (fgets(&linebuf[read_pos], (int)read_sz, f) == NULL) 261
246 done_reading = 1; 262 if (fgets(&linebuf[read_pos], (int)read_sz, filePointer) == NULL) {
247 else { 263 done_reading = true;
264 } else {
248 read_pos = strlen(linebuf); 265 read_pos = strlen(linebuf);
249 if (linebuf[read_pos - 1] == '\n') { 266 if (linebuf[read_pos - 1] == '\n') {
250 linebuf[--read_pos] = '\0'; 267 linebuf[--read_pos] = '\0';
251 done_reading = 1; 268 done_reading = true;
252 } 269 }
253 } 270 }
254 } 271 }
255 lineend = &linebuf[read_pos]; 272
273 char *lineend = &linebuf[read_pos];
256 /* all that to read one line, isn't C fun? :) now comes the parsing :/ */ 274 /* all that to read one line, isn't C fun? :) now comes the parsing :/ */
257 275
258 /* skip leading whitespace */ 276 /* skip leading whitespace */
259 for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) 277 char *optptr = NULL;
260 continue; 278 for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) {
279 }
280
261 /* continue to '=' or EOL, watching for spaces that might precede it */ 281 /* continue to '=' or EOL, watching for spaces that might precede it */
282 char *eqptr = NULL;
283 char *optend = NULL;
262 for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) { 284 for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) {
263 if (isspace(*eqptr) && optend == NULL) 285 if (isspace(*eqptr) && optend == NULL) {
264 optend = eqptr; 286 optend = eqptr;
265 else 287 } else {
266 optend = NULL; 288 optend = NULL;
289 }
267 } 290 }
268 if (optend == NULL) 291
292 if (optend == NULL) {
269 optend = eqptr; 293 optend = eqptr;
294 }
295
270 --optend; 296 --optend;
297
271 /* ^[[:space:]]*=foo is a syntax error */ 298 /* ^[[:space:]]*=foo is a syntax error */
272 if (optptr == eqptr) 299 if (optptr == eqptr) {
273 die(STATE_UNKNOWN, "%s\n", _("Config file error")); 300 die(STATE_UNKNOWN, "%s\n", _("Config file error"));
301 }
302
274 /* continue from '=' to start of value or EOL */ 303 /* continue from '=' to start of value or EOL */
275 for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) 304 char *valptr = NULL;
276 continue; 305 for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) {
306 }
307
277 /* continue to the end of value */ 308 /* continue to the end of value */
278 for (valend = valptr; valend < lineend; valend++) 309 char *valend = NULL;
279 continue; 310 for (valend = valptr; valend < lineend; valend++) {
311 }
312
280 --valend; 313 --valend;
314
281 /* finally trim off trailing spaces */ 315 /* finally trim off trailing spaces */
282 for (; isspace(*valend); valend--) 316 for (; isspace(*valend); valend--) {
283 continue; 317 }
318
284 /* calculate the length of "--foo" */ 319 /* calculate the length of "--foo" */
285 opt_len = (size_t)(1 + optend - optptr); 320 size_t opt_len = (size_t)(1 + optend - optptr);
286 /* 1-character params needs only one dash */ 321 /* 1-character params needs only one dash */
287 if (opt_len == 1) 322 size_t cfg_len = 0;
323 if (opt_len == 1) {
288 cfg_len = 1 + (opt_len); 324 cfg_len = 1 + (opt_len);
289 else 325 } else {
290 cfg_len = 2 + (opt_len); 326 cfg_len = 2 + (opt_len);
327 }
328
329 size_t val_len = 0;
330 bool equals = false;
331 bool value = false;
291 /* if valptr<lineend then we have to also allocate space for "=bar" */ 332 /* if valptr<lineend then we have to also allocate space for "=bar" */
292 if (valptr < lineend) { 333 if (valptr < lineend) {
293 equals = value = 1; 334 equals = value = true;
294 val_len = (size_t)(1 + valend - valptr); 335 val_len = (size_t)(1 + valend - valptr);
295 cfg_len += 1 + val_len; 336 cfg_len += 1 + val_len;
296 } 337 } else if (valptr == lineend) {
297 /* if valptr==valend then we have "=" but no "bar" */ 338 /* if valptr==valend then we have "=" but no "bar" */
298 else if (valptr == lineend) { 339 equals = true;
299 equals = 1;
300 cfg_len += 1; 340 cfg_len += 1;
301 } 341 }
342
302 /* a line with no equal sign isn't valid */ 343 /* a line with no equal sign isn't valid */
303 if (equals == 0) 344 if (!equals) {
304 die(STATE_UNKNOWN, "%s\n", _("Config file error")); 345 die(STATE_UNKNOWN, "%s\n", _("Config file error"));
346 }
305 347
306 /* okay, now we have all the info we need, so we create a new np_arg_list 348 /* okay, now we have all the info we need, so we create a new np_arg_list
307 * element and set the argument... 349 * element and set the argument...
308 */ 350 */
309 optnew = malloc(sizeof(np_arg_list)); 351 np_arg_list *optnew = malloc(sizeof(np_arg_list));
310 optnew->next = NULL; 352 optnew->next = NULL;
311 353
312 read_pos = 0; 354 read_pos = 0;
@@ -329,11 +371,13 @@ static int add_option(FILE *f, np_arg_list **optlst) {
329 optnew->arg[read_pos] = '\0'; 371 optnew->arg[read_pos] = '\0';
330 372
331 /* ...and put that to the end of the list */ 373 /* ...and put that to the end of the list */
332 if (*optlst == NULL) 374 if (*optlst == NULL) {
333 *optlst = optnew; 375 *optlst = optnew;
334 else { 376 } else {
335 while (opttmp->next != NULL) 377 np_arg_list *opttmp = *optlst;
378 while (opttmp->next != NULL) {
336 opttmp = opttmp->next; 379 opttmp = opttmp->next;
380 }
337 opttmp->next = optnew; 381 opttmp->next = optnew;
338 } 382 }
339 383
@@ -344,7 +388,8 @@ static int add_option(FILE *f, np_arg_list **optlst) {
344static char *default_file(void) { 388static char *default_file(void) {
345 char *ini_file; 389 char *ini_file;
346 390
347 if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL || (ini_file = default_file_in_path()) != NULL) { 391 if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL ||
392 (ini_file = default_file_in_path()) != NULL) {
348 return ini_file; 393 return ini_file;
349 } 394 }
350 395
@@ -357,19 +402,25 @@ static char *default_file(void) {
357} 402}
358 403
359static char *default_file_in_path(void) { 404static char *default_file_in_path(void) {
360 char *config_path, **file; 405 char *config_path;
361 char *dir, *ini_file, *tokens; 406 char **file;
407 char *dir;
408 char *ini_file;
409 char *tokens;
362 410
363 if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) 411 if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) {
364 return NULL; 412 return NULL;
413 }
365 /* shall we spit out a warning that NAGIOS_CONFIG_PATH is deprecated? */ 414 /* shall we spit out a warning that NAGIOS_CONFIG_PATH is deprecated? */
366 415
367 if ((tokens = strdup(config_path)) == NULL) 416 if ((tokens = strdup(config_path)) == NULL) {
368 die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); 417 die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory"));
418 }
369 for (dir = strtok(tokens, ":"); dir != NULL; dir = strtok(NULL, ":")) { 419 for (dir = strtok(tokens, ":"); dir != NULL; dir = strtok(NULL, ":")) {
370 for (file = default_ini_file_names; *file != NULL; file++) { 420 for (file = default_ini_file_names; *file != NULL; file++) {
371 if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0) 421 if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0) {
372 die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); 422 die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory"));
423 }
373 if (access(ini_file, F_OK) == 0) { 424 if (access(ini_file, F_OK) == 0) {
374 free(tokens); 425 free(tokens);
375 return ini_file; 426 return ini_file;
diff --git a/lib/perfdata.c b/lib/perfdata.c
new file mode 100644
index 00000000..2930a8bc
--- /dev/null
+++ b/lib/perfdata.c
@@ -0,0 +1,649 @@
1#include "./perfdata.h"
2#include "../plugins/common.h"
3#include "../plugins/utils.h"
4#include "utils_base.h"
5
6#include <assert.h>
7#include <limits.h>
8#include <stdlib.h>
9
10char *pd_value_to_string(const mp_perfdata_value pd) {
11 char *result = NULL;
12
13 assert(pd.type != PD_TYPE_NONE);
14
15 switch (pd.type) {
16 case PD_TYPE_INT:
17 asprintf(&result, "%lli", pd.pd_int);
18 break;
19 case PD_TYPE_UINT:
20 asprintf(&result, "%llu", pd.pd_int);
21 break;
22 case PD_TYPE_DOUBLE:
23 asprintf(&result, "%f", pd.pd_double);
24 break;
25 default:
26 // die here
27 die(STATE_UNKNOWN, "Invalid mp_perfdata mode\n");
28 }
29
30 return result;
31}
32
33char *pd_to_string(mp_perfdata pd) {
34 assert(pd.label != NULL);
35 char *result = NULL;
36
37 if (strchr(pd.label, '\'') == NULL) {
38 asprintf(&result, "'%s'=", pd.label);
39 } else {
40 // we have a illegal single quote in the string
41 // replace it silently instead of complaining
42 for (char *ptr = pd.label; *ptr == '\0'; ptr++) {
43 if (*ptr == '\'') {
44 *ptr = '_';
45 }
46 }
47 }
48
49 asprintf(&result, "%s%s", result, pd_value_to_string(pd.value));
50
51 if (pd.uom != NULL) {
52 asprintf(&result, "%s%s", result, pd.uom);
53 }
54
55 if (pd.warn_present) {
56 asprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn));
57 } else {
58 asprintf(&result, "%s;", result);
59 }
60
61 if (pd.crit_present) {
62 asprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit));
63 } else {
64 asprintf(&result, "%s;", result);
65 }
66 if (pd.min_present) {
67 asprintf(&result, "%s;%s", result, pd_value_to_string(pd.min));
68 } else {
69 asprintf(&result, "%s;", result);
70 }
71
72 if (pd.max_present) {
73 asprintf(&result, "%s;%s", result, pd_value_to_string(pd.max));
74 }
75
76 /*printf("pd_to_string: %s\n", result); */
77
78 return result;
79}
80
81char *pd_list_to_string(const pd_list pd) {
82 char *result = pd_to_string(pd.data);
83
84 for (pd_list *elem = pd.next; elem != NULL; elem = elem->next) {
85 asprintf(&result, "%s %s", result, pd_to_string(elem->data));
86 }
87
88 return result;
89}
90
91mp_perfdata perfdata_init() {
92 mp_perfdata pd = {};
93 return pd;
94}
95
96pd_list *pd_list_init() {
97 pd_list *tmp = (pd_list *)calloc(1, sizeof(pd_list));
98 if (tmp == NULL) {
99 die(STATE_UNKNOWN, "calloc failed\n");
100 }
101 tmp->next = NULL;
102 return tmp;
103}
104
105mp_range mp_range_init() {
106 mp_range result = {
107 .alert_on_inside_range = OUTSIDE,
108 .start = {},
109 .start_infinity = true,
110 .end = {},
111 .end_infinity = true,
112 };
113
114 return result;
115}
116
117mp_range mp_range_set_start(mp_range input, mp_perfdata_value perf_val) {
118 input.start = perf_val;
119 input.start_infinity = false;
120 return input;
121}
122
123mp_range mp_range_set_end(mp_range input, mp_perfdata_value perf_val) {
124 input.end = perf_val;
125 input.end_infinity = false;
126 return input;
127}
128
129void pd_list_append(pd_list pdl[1], const mp_perfdata pd) {
130 assert(pdl != NULL);
131
132 if (pdl->data.value.type == PD_TYPE_NONE) {
133 // first entry is still empty
134 pdl->data = pd;
135 } else {
136 // find last element in the list
137 pd_list *curr = pdl;
138 pd_list *next = pdl->next;
139
140 while (next != NULL) {
141 curr = next;
142 next = next->next;
143 }
144
145 if (curr->data.value.type == PD_TYPE_NONE) {
146 // still empty
147 curr->data = pd;
148 } else {
149 // new a new one
150 curr->next = pd_list_init();
151 curr->next->data = pd;
152 }
153 }
154}
155
156void pd_list_free(pd_list pdl[1]) {
157 while (pdl != NULL) {
158 pd_list *old = pdl;
159 pdl = pdl->next;
160 free(old);
161 }
162}
163
164/*
165 * returns -1 if a < b, 0 if a == b, 1 if a > b
166 */
167int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) {
168 // Test if types are different
169 if (a.type == b.type) {
170
171 switch (a.type) {
172 case PD_TYPE_UINT:
173 if (a.pd_uint < b.pd_uint) {
174 return -1;
175 } else if (a.pd_uint == b.pd_uint) {
176 return 0;
177 } else {
178 return 1;
179 }
180 break;
181 case PD_TYPE_INT:
182 if (a.pd_int < b.pd_int) {
183 return -1;
184 } else if (a.pd_int == b.pd_int) {
185 return 0;
186 } else {
187 return 1;
188 }
189 break;
190 case PD_TYPE_DOUBLE:
191 if (a.pd_int < b.pd_int) {
192 return -1;
193 } else if (a.pd_int == b.pd_int) {
194 return 0;
195 } else {
196 return 1;
197 }
198 break;
199 default:
200 die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
201 }
202 }
203
204 // Get dirty here
205 long double floating_a = 0;
206
207 switch (a.type) {
208 case PD_TYPE_UINT:
209 floating_a = a.pd_uint;
210 break;
211 case PD_TYPE_INT:
212 floating_a = a.pd_int;
213 break;
214 case PD_TYPE_DOUBLE:
215 floating_a = a.pd_double;
216 break;
217 default:
218 die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
219 }
220
221 long double floating_b = 0;
222 switch (b.type) {
223 case PD_TYPE_UINT:
224 floating_b = b.pd_uint;
225 break;
226 case PD_TYPE_INT:
227 floating_b = b.pd_int;
228 break;
229 case PD_TYPE_DOUBLE:
230 floating_b = b.pd_double;
231 break;
232 default:
233 die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
234 }
235
236 if (floating_a < floating_b) {
237 return -1;
238 }
239 if (floating_a == floating_b) {
240 return 0;
241 }
242 return 1;
243}
244
245char *mp_range_to_string(const mp_range input) {
246 char *result = "";
247 if (input.alert_on_inside_range == INSIDE) {
248 asprintf(&result, "@");
249 }
250
251 if (input.start_infinity) {
252 asprintf(&result, "%s~:", result);
253 } else {
254 asprintf(&result, "%s%s:", result, pd_value_to_string(input.start));
255 }
256
257 if (!input.end_infinity) {
258 asprintf(&result, "%s%s", result, pd_value_to_string(input.end));
259 }
260 return result;
261}
262
263mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) {
264 return mp_set_pd_value_double(pd, value);
265}
266
267mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
268 pd.value.pd_double = value;
269 pd.value.type = PD_TYPE_DOUBLE;
270 return pd;
271}
272
273mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) {
274 return mp_set_pd_value_long_long(pd, (long long)value);
275}
276
277mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) {
278 return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
279}
280
281mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) {
282 return mp_set_pd_value_long_long(pd, (long long)value);
283}
284
285mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) {
286 return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
287}
288
289mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) {
290 return mp_set_pd_value_long_long(pd, (long long)value);
291}
292
293mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) {
294 return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
295}
296
297mp_perfdata mp_set_pd_value_long_long(mp_perfdata pd, long long value) {
298 pd.value.pd_int = value;
299 pd.value.type = PD_TYPE_INT;
300 return pd;
301}
302
303mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata pd, unsigned long long value) {
304 pd.value.pd_uint = value;
305 pd.value.type = PD_TYPE_UINT;
306 return pd;
307}
308
309mp_perfdata_value mp_create_pd_value_double(double value) {
310 mp_perfdata_value res = {0};
311 res.type = PD_TYPE_DOUBLE;
312 res.pd_double = value;
313 return res;
314}
315
316mp_perfdata_value mp_create_pd_value_float(float value) {
317 return mp_create_pd_value_double((double)value);
318}
319
320mp_perfdata_value mp_create_pd_value_char(char value) {
321 return mp_create_pd_value_long_long((long long)value);
322}
323
324mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) {
325 return mp_create_pd_value_u_long_long((unsigned long long)value);
326}
327
328mp_perfdata_value mp_create_pd_value_int(int value) {
329 return mp_create_pd_value_long_long((long long)value);
330}
331
332mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) {
333 return mp_create_pd_value_u_long_long((unsigned long long)value);
334}
335
336mp_perfdata_value mp_create_pd_value_long(long value) {
337 return mp_create_pd_value_long_long((long long)value);
338}
339
340mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) {
341 return mp_create_pd_value_u_long_long((unsigned long long)value);
342}
343
344mp_perfdata_value mp_create_pd_value_long_long(long long value) {
345 mp_perfdata_value res = {0};
346 res.type = PD_TYPE_INT;
347 res.pd_int = value;
348 return res;
349}
350
351mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long value) {
352 mp_perfdata_value res = {0};
353 res.type = PD_TYPE_UINT;
354 res.pd_uint = value;
355 return res;
356}
357
358char *fmt_range(range foo) { return foo.text; }
359
360typedef struct integer_parser_wrapper {
361 int error;
362 mp_perfdata_value value;
363} integer_parser_wrapper;
364
365typedef struct double_parser_wrapper {
366 int error;
367 mp_perfdata_value value;
368} double_parser_wrapper;
369
370typedef struct perfdata_value_parser_wrapper {
371 int error;
372 mp_perfdata_value value;
373} perfdata_value_parser_wrapper;
374
375double_parser_wrapper parse_double(const char *input);
376integer_parser_wrapper parse_integer(const char *input);
377perfdata_value_parser_wrapper parse_pd_value(const char *input);
378
379mp_range_parsed mp_parse_range_string(const char *input) {
380 if (input == NULL) {
381 mp_range_parsed result = {
382 .error = MP_RANGE_PARSING_FAILURE,
383 };
384 return result;
385 }
386
387 if (strlen(input) == 0) {
388 mp_range_parsed result = {
389 .error = MP_RANGE_PARSING_FAILURE,
390 };
391 return result;
392 }
393
394 mp_range_parsed result = {
395 .range = mp_range_init(),
396 .error = MP_PARSING_SUCCES,
397 };
398
399 if (input[0] == '@') {
400 // found an '@' at beginning, so invert the range logic
401 result.range.alert_on_inside_range = INSIDE;
402
403 // advance the pointer one symbol
404 input++;
405 }
406
407 char *working_copy = strdup(input);
408 if (working_copy == NULL) {
409 // strdup error, probably
410 mp_range_parsed result = {
411 .error = MP_RANGE_PARSING_FAILURE,
412 };
413 return result;
414 }
415 input = working_copy;
416
417 char *separator = index(working_copy, ':');
418 if (separator != NULL) {
419 // Found a separator
420 // set the separator to 0, so we have two different strings
421 *separator = '\0';
422
423 if (input[0] == '~') {
424 // the beginning starts with '~', so it might be infinity
425 if (&input[1] != separator) {
426 // the next symbol after '~' is not the separator!
427 // so input is probably wrong
428 result.error = MP_RANGE_PARSING_FAILURE;
429 free(working_copy);
430 return result;
431 }
432
433 result.range.start_infinity = true;
434 } else {
435 // No '~' at the beginning, so this should be a number
436 result.range.start_infinity = false;
437 perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
438
439 if (parsed_pd.error != MP_PARSING_SUCCES) {
440 result.error = parsed_pd.error;
441 free(working_copy);
442 return result;
443 }
444
445 result.range.start = parsed_pd.value;
446 result.range.start_infinity = false;
447 }
448 // got the first part now
449 // advance the pointer
450 input = separator + 1;
451 }
452
453 // End part or no separator
454 if (input[0] == '\0') {
455 // the end is infinite
456 result.range.end_infinity = true;
457 } else {
458 perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
459
460 if (parsed_pd.error != MP_PARSING_SUCCES) {
461 result.error = parsed_pd.error;
462 return result;
463 }
464 result.range.end = parsed_pd.value;
465 result.range.end_infinity = false;
466 }
467 free(working_copy);
468 return result;
469}
470
471double_parser_wrapper parse_double(const char *input) {
472 double_parser_wrapper result = {
473 .error = MP_PARSING_SUCCES,
474 };
475
476 if (input == NULL) {
477 result.error = MP_PARSING_FAILURE;
478 return result;
479 }
480
481 char *endptr = NULL;
482 errno = 0;
483 double tmp = strtod(input, &endptr);
484
485 if (input == endptr) {
486 // man 3 strtod says, no conversion performed
487 result.error = MP_PARSING_FAILURE;
488 return result;
489 }
490
491 if (errno) {
492 // some other error
493 // TODO maybe differentiate a little bit
494 result.error = MP_PARSING_FAILURE;
495 return result;
496 }
497
498 result.value = mp_create_pd_value(tmp);
499 return result;
500}
501
502integer_parser_wrapper parse_integer(const char *input) {
503 integer_parser_wrapper result = {
504 .error = MP_PARSING_SUCCES,
505 };
506
507 if (input == NULL) {
508 result.error = MP_PARSING_FAILURE;
509 return result;
510 }
511
512 char *endptr = NULL;
513 errno = 0;
514 long long tmp = strtoll(input, &endptr, 0);
515
516 // validating *sigh*
517 if (*endptr != '\0') {
518 // something went wrong in strtoll
519 if (tmp == LLONG_MIN) {
520 // underflow
521 result.error = MP_RANGE_PARSING_UNDERFLOW;
522 return result;
523 }
524
525 if (tmp == LLONG_MAX) {
526 // overflow
527 result.error = MP_RANGE_PARSING_OVERFLOW;
528 return result;
529 }
530
531 // still wrong, but not sure why, probably invalid characters
532 if (errno == EINVAL) {
533 result.error = MP_RANGE_PARSING_INVALID_CHAR;
534 return result;
535 }
536
537 // some other error, do catch all here
538 result.error = MP_RANGE_PARSING_FAILURE;
539 return result;
540 }
541
542 // no error, should be fine
543 result.value = mp_create_pd_value(tmp);
544 return result;
545}
546
547perfdata_value_parser_wrapper parse_pd_value(const char *input) {
548 // try integer first
549 integer_parser_wrapper tmp_int = parse_integer(input);
550
551 if (tmp_int.error == MP_PARSING_SUCCES) {
552 perfdata_value_parser_wrapper result = {
553 .error = tmp_int.error,
554 .value = tmp_int.value,
555 };
556 return result;
557 }
558
559 double_parser_wrapper tmp_double = parse_double(input);
560 perfdata_value_parser_wrapper result = {};
561 if (tmp_double.error == MP_PARSING_SUCCES) {
562 result.error = tmp_double.error;
563 result.value = tmp_double.value;
564 } else {
565 result.error = tmp_double.error;
566 }
567 return result;
568}
569
570mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value) {
571 perfdata.max = value;
572 perfdata.max_present = true;
573 return perfdata;
574}
575
576mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value) {
577 perfdata.min = value;
578 perfdata.min_present = true;
579 return perfdata;
580}
581
582double mp_get_pd_value(mp_perfdata_value value) {
583 assert(value.type != PD_TYPE_NONE);
584 switch (value.type) {
585 case PD_TYPE_DOUBLE:
586 return value.pd_double;
587 case PD_TYPE_INT:
588 return (double)value.pd_int;
589 case PD_TYPE_UINT:
590 return (double)value.pd_uint;
591 default:
592 return 0; // just to make the compiler happy
593 }
594}
595
596mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right) {
597 if (left.type == right.type) {
598 switch (left.type) {
599 case PD_TYPE_DOUBLE:
600 left.pd_double *= right.pd_double;
601 return left;
602 case PD_TYPE_INT:
603 left.pd_int *= right.pd_int;
604 return left;
605 case PD_TYPE_UINT:
606 left.pd_uint *= right.pd_uint;
607 return left;
608 default:
609 // what to here?
610 return left;
611 }
612 }
613
614 // Different types, oh boy, just do the lazy thing for now and switch to double
615 switch (left.type) {
616 case PD_TYPE_INT:
617 left.pd_double = (double)left.pd_int;
618 left.type = PD_TYPE_DOUBLE;
619 break;
620 case PD_TYPE_UINT:
621 left.pd_double = (double)left.pd_uint;
622 left.type = PD_TYPE_DOUBLE;
623 break;
624 }
625
626 switch (right.type) {
627 case PD_TYPE_INT:
628 right.pd_double = (double)right.pd_int;
629 right.type = PD_TYPE_DOUBLE;
630 break;
631 case PD_TYPE_UINT:
632 right.pd_double = (double)right.pd_uint;
633 right.type = PD_TYPE_DOUBLE;
634 break;
635 }
636
637 left.pd_double *= right.pd_double;
638 return left;
639}
640
641mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor) {
642 if (!range.end_infinity) {
643 range.end = mp_pd_value_multiply(range.end, factor);
644 }
645 if (!range.start_infinity) {
646 range.start = mp_pd_value_multiply(range.start, factor);
647 }
648 return range;
649}
diff --git a/lib/perfdata.h b/lib/perfdata.h
new file mode 100644
index 00000000..e51ef5fd
--- /dev/null
+++ b/lib/perfdata.h
@@ -0,0 +1,219 @@
1#pragma once
2
3#include "../config.h"
4
5#include <inttypes.h>
6#include <stdbool.h>
7
8// Enum for the specific type of a perfdata_value
9typedef enum pd_value_type {
10 PD_TYPE_NONE = 0,
11 PD_TYPE_INT,
12 PD_TYPE_UINT,
13 PD_TYPE_DOUBLE
14} pd_value_type;
15
16typedef struct {
17 enum pd_value_type type;
18 union {
19 long long pd_int;
20 unsigned long long pd_uint;
21 double pd_double;
22 };
23} mp_perfdata_value;
24
25#define MP_OUTSIDE false
26#define MP_INSIDE true
27
28/*
29 * New range type with generic numerical values
30 */
31typedef struct {
32 mp_perfdata_value start;
33 bool start_infinity; /* false (default) or true */
34
35 mp_perfdata_value end;
36 bool end_infinity;
37
38 bool alert_on_inside_range; /* OUTSIDE (default) or INSIDE */
39} mp_range;
40
41/*
42 * Old range type with floating point values
43 */
44typedef struct {
45 double start;
46 bool start_infinity;
47 double end;
48 bool end_infinity;
49 int alert_on; /* OUTSIDE (default) or INSIDE */
50 char *text; /* original unparsed text input */
51} range;
52
53/*
54 * Perfdata type for storing perfdata output
55 */
56typedef struct {
57 char *label;
58 char *uom;
59 mp_perfdata_value value;
60
61 bool warn_present;
62 mp_range warn;
63
64 bool crit_present;
65 mp_range crit;
66
67 bool min_present;
68 mp_perfdata_value min;
69
70 bool max_present;
71 mp_perfdata_value max;
72} mp_perfdata;
73
74/*
75 * List of mp_perfdata values
76 */
77typedef struct pd_list_struct {
78 mp_perfdata data;
79 struct pd_list_struct *next;
80} pd_list;
81
82/*
83 * ============
84 * Initializers
85 * ============
86 */
87/*
88 * Initialize mp_perfdata value. Always use this to generate a new one
89 */
90mp_perfdata perfdata_init(void);
91
92/*
93 * Initialize pd_list value. Always use this to generate a new one
94 */
95pd_list *pd_list_init(void);
96
97/*
98 * Initialize a new mp_range value, with unset values (start and end are infinite
99 */
100mp_range mp_range_init(void);
101
102/*
103 * Worker functions
104 */
105
106mp_range mp_range_set_start(mp_range, mp_perfdata_value);
107mp_range mp_range_set_end(mp_range, mp_perfdata_value);
108
109/*
110 * Parsing a range from a string
111 */
112
113typedef enum {
114 MP_PARSING_SUCCES = 0,
115 MP_PARSING_FAILURE,
116 MP_RANGE_PARSING_FAILURE,
117 MP_RANGE_PARSING_UNDERFLOW,
118 MP_RANGE_PARSING_OVERFLOW,
119 MP_RANGE_PARSING_INVALID_CHAR,
120} mp_range_parser_error;
121
122typedef struct mp_range_parsed {
123 mp_range_parser_error error;
124 mp_range range;
125} mp_range_parsed;
126
127mp_range_parsed mp_parse_range_string(const char * /*input*/);
128
129/*
130 * Appends a mp_perfdata value to a pd_list
131 */
132void pd_list_append(pd_list[1], mp_perfdata);
133
134#define mp_set_pd_value(P, V) \
135 _Generic((V), \
136 float: mp_set_pd_value_float, \
137 double: mp_set_pd_value_double, \
138 int: mp_set_pd_value_int, \
139 unsigned int: mp_set_pd_value_u_int, \
140 long: mp_set_pd_value_long, \
141 unsigned long: mp_set_pd_value_u_long, \
142 long long: mp_set_pd_value_long_long, \
143 unsigned long long: mp_set_pd_value_u_long_long)(P, V)
144
145mp_perfdata mp_set_pd_value_float(mp_perfdata, float);
146mp_perfdata mp_set_pd_value_double(mp_perfdata, double);
147mp_perfdata mp_set_pd_value_int(mp_perfdata, int);
148mp_perfdata mp_set_pd_value_u_int(mp_perfdata, unsigned int);
149mp_perfdata mp_set_pd_value_long(mp_perfdata, long);
150mp_perfdata mp_set_pd_value_u_long(mp_perfdata, unsigned long);
151mp_perfdata mp_set_pd_value_long_long(mp_perfdata, long long);
152mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
153
154#define mp_create_pd_value(V) \
155 _Generic((V), \
156 float: mp_create_pd_value_float, \
157 double: mp_create_pd_value_double, \
158 char: mp_create_pd_value_char, \
159 unsigned char: mp_create_pd_value_u_char, \
160 int: mp_create_pd_value_int, \
161 unsigned int: mp_create_pd_value_u_int, \
162 long: mp_create_pd_value_long, \
163 unsigned long: mp_create_pd_value_u_long, \
164 long long: mp_create_pd_value_long_long, \
165 unsigned long long: mp_create_pd_value_u_long_long)(V)
166
167mp_perfdata_value mp_create_pd_value_float(float);
168mp_perfdata_value mp_create_pd_value_double(double);
169mp_perfdata_value mp_create_pd_value_char(char);
170mp_perfdata_value mp_create_pd_value_u_char(unsigned char);
171mp_perfdata_value mp_create_pd_value_int(int);
172mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
173mp_perfdata_value mp_create_pd_value_long(long);
174mp_perfdata_value mp_create_pd_value_u_long(unsigned long);
175mp_perfdata_value mp_create_pd_value_long_long(long long);
176mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long);
177
178mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value);
179mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value);
180
181double mp_get_pd_value(mp_perfdata_value value);
182
183/*
184 * Free the memory used by a pd_list
185 */
186void pd_list_free(pd_list[1]);
187
188int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value);
189
190// ================
191// Helper functions
192// ================
193
194mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right);
195mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor);
196
197// =================
198// String formatters
199// =================
200/*
201 * Generate string from mp_perfdata value
202 */
203char *pd_to_string(mp_perfdata);
204
205/*
206 * Generate string from perfdata_value value
207 */
208char *pd_value_to_string(mp_perfdata_value);
209
210/*
211 * Generate string from pd_list value for the final output
212 */
213char *pd_list_to_string(pd_list);
214
215/*
216 * Generate string from a mp_range value
217 */
218char *mp_range_to_string(mp_range);
219char *fmt_range(range);
diff --git a/lib/states.h b/lib/states.h
new file mode 100644
index 00000000..4a170caa
--- /dev/null
+++ b/lib/states.h
@@ -0,0 +1,71 @@
1#ifndef _MP_STATES_
2#define _MP_STATES_
3
4#include "../config.h"
5#include <sys/param.h>
6
7typedef enum state_enum {
8 STATE_OK,
9 STATE_WARNING,
10 STATE_CRITICAL,
11 STATE_UNKNOWN,
12 STATE_DEPENDENT
13} mp_state_enum;
14
15/* **************************************************************************
16 * max_state(STATE_x, STATE_y)
17 * compares STATE_x to STATE_y and returns result based on the following
18 * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
19 *
20 * Note that numerically the above does not hold
21 ****************************************************************************/
22
23static inline mp_state_enum max_state(mp_state_enum a, mp_state_enum b) {
24 if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
25 return STATE_CRITICAL;
26 }
27 if (a == STATE_WARNING || b == STATE_WARNING) {
28 return STATE_WARNING;
29 }
30 if (a == STATE_OK || b == STATE_OK) {
31 return STATE_OK;
32 }
33 if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
34 return STATE_UNKNOWN;
35 }
36 if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
37 return STATE_DEPENDENT;
38 }
39 return MAX(a, b);
40}
41
42/* **************************************************************************
43 * max_state_alt(STATE_x, STATE_y)
44 * compares STATE_x to STATE_y and returns result based on the following
45 * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
46 *
47 * The main difference between max_state_alt and max_state it that it doesn't
48 * allow setting a default to UNKNOWN. It will instead prioritixe any valid
49 * non-OK state.
50 ****************************************************************************/
51
52static inline mp_state_enum max_state_alt(mp_state_enum a, mp_state_enum b) {
53 if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
54 return STATE_CRITICAL;
55 }
56 if (a == STATE_WARNING || b == STATE_WARNING) {
57 return STATE_WARNING;
58 }
59 if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
60 return STATE_UNKNOWN;
61 }
62 if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
63 return STATE_DEPENDENT;
64 }
65 if (a == STATE_OK || b == STATE_OK) {
66 return STATE_OK;
67 }
68 return MAX(a, b);
69}
70
71#endif
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am
index 31d79df6..7798a72e 100644
--- a/lib/tests/Makefile.am
+++ b/lib/tests/Makefile.am
@@ -8,9 +8,9 @@ check_PROGRAMS = @EXTRA_TEST@
8AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 8AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
9 -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 9 -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
10 10
11EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 11EXTRA_PROGRAMS = test_utils test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output
12 12
13np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t 13np_test_scripts = test_base64.t test_cmd.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t
14np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini 14np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini
15EXTRA_DIST = $(np_test_scripts) $(np_test_files) var 15EXTRA_DIST = $(np_test_scripts) $(np_test_files) var
16 16
@@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags)
29AM_LDFLAGS = $(tap_ldflags) -ltap 29AM_LDFLAGS = $(tap_ldflags) -ltap
30LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO) 30LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO)
31 31
32SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c 32SOURCES = test_utils.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c
33 33
34test: ${noinst_PROGRAMS} 34test: ${noinst_PROGRAMS}
35 perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS) 35 perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS)
diff --git a/lib/tests/test_base64.c b/lib/tests/test_base64.c
index 94cb5aa9..798244da 100644
--- a/lib/tests/test_base64.c
+++ b/lib/tests/test_base64.c
@@ -180,117 +180,168 @@ int main(int argc, char **argv) {
180#endif 180#endif
181 181
182 char random[1024] = { 182 char random[1024] = {
183 0x0b, 0x30, 0x44, 0x62, 0x7c, 0x22, 0x1f, 0x0d, 0x05, 0x67, 0x2c, 0x2a, 0x39, 0x21, 0x46, 0x08, 0x50, 0x66, 0x34, 0x37, 0x0b, 0x45, 183 0x0b, 0x30, 0x44, 0x62, 0x7c, 0x22, 0x1f, 0x0d, 0x05, 0x67, 0x2c, 0x2a, 0x39, 0x21, 0x46,
184 0x4b, 0x38, 0x32, 0x06, 0x7a, 0x3e, 0x7f, 0x0c, 0x40, 0x18, 0x6b, 0x2d, 0x60, 0x4c, 0x60, 0x0c, 0x23, 0x43, 0x3b, 0x3e, 0x1b, 0x16, 184 0x08, 0x50, 0x66, 0x34, 0x37, 0x0b, 0x45, 0x4b, 0x38, 0x32, 0x06, 0x7a, 0x3e, 0x7f, 0x0c,
185 0x04, 0x46, 0x58, 0x3f, 0x40, 0x6a, 0x11, 0x05, 0x63, 0x71, 0x14, 0x35, 0x47, 0x79, 0x13, 0x6f, 0x6b, 0x27, 0x18, 0x5b, 0x48, 0x27, 185 0x40, 0x18, 0x6b, 0x2d, 0x60, 0x4c, 0x60, 0x0c, 0x23, 0x43, 0x3b, 0x3e, 0x1b, 0x16, 0x04,
186 0x3e, 0x6f, 0x15, 0x33, 0x4f, 0x3e, 0x5e, 0x51, 0x73, 0x68, 0x25, 0x0f, 0x06, 0x5b, 0x7c, 0x72, 0x75, 0x3e, 0x3f, 0x1b, 0x5c, 0x6d, 186 0x46, 0x58, 0x3f, 0x40, 0x6a, 0x11, 0x05, 0x63, 0x71, 0x14, 0x35, 0x47, 0x79, 0x13, 0x6f,
187 0x6a, 0x39, 0x7c, 0x63, 0x63, 0x60, 0x6c, 0x7a, 0x33, 0x76, 0x52, 0x13, 0x25, 0x33, 0x7d, 0x65, 0x23, 0x27, 0x11, 0x06, 0x06, 0x47, 187 0x6b, 0x27, 0x18, 0x5b, 0x48, 0x27, 0x3e, 0x6f, 0x15, 0x33, 0x4f, 0x3e, 0x5e, 0x51, 0x73,
188 0x71, 0x1e, 0x14, 0x74, 0x63, 0x70, 0x2d, 0x15, 0x27, 0x18, 0x51, 0x06, 0x05, 0x33, 0x11, 0x2c, 0x6b, 0x00, 0x2d, 0x77, 0x20, 0x48, 188 0x68, 0x25, 0x0f, 0x06, 0x5b, 0x7c, 0x72, 0x75, 0x3e, 0x3f, 0x1b, 0x5c, 0x6d, 0x6a, 0x39,
189 0x0d, 0x73, 0x51, 0x45, 0x25, 0x7f, 0x7f, 0x35, 0x26, 0x2e, 0x26, 0x53, 0x24, 0x68, 0x1e, 0x0e, 0x58, 0x3a, 0x59, 0x50, 0x56, 0x37, 189 0x7c, 0x63, 0x63, 0x60, 0x6c, 0x7a, 0x33, 0x76, 0x52, 0x13, 0x25, 0x33, 0x7d, 0x65, 0x23,
190 0x5f, 0x66, 0x01, 0x4c, 0x5a, 0x64, 0x32, 0x50, 0x7b, 0x6a, 0x20, 0x72, 0x2b, 0x1d, 0x7e, 0x43, 0x7b, 0x61, 0x42, 0x0b, 0x61, 0x73, 190 0x27, 0x11, 0x06, 0x06, 0x47, 0x71, 0x1e, 0x14, 0x74, 0x63, 0x70, 0x2d, 0x15, 0x27, 0x18,
191 0x24, 0x79, 0x3a, 0x6b, 0x4a, 0x79, 0x6e, 0x09, 0x0f, 0x27, 0x2d, 0x0c, 0x5e, 0x32, 0x4b, 0x0d, 0x79, 0x46, 0x39, 0x21, 0x0a, 0x26, 191 0x51, 0x06, 0x05, 0x33, 0x11, 0x2c, 0x6b, 0x00, 0x2d, 0x77, 0x20, 0x48, 0x0d, 0x73, 0x51,
192 0x5f, 0x3a, 0x00, 0x26, 0x3f, 0x13, 0x2e, 0x7e, 0x50, 0x2b, 0x67, 0x46, 0x72, 0x3f, 0x3b, 0x01, 0x46, 0x1b, 0x0b, 0x35, 0x49, 0x39, 192 0x45, 0x25, 0x7f, 0x7f, 0x35, 0x26, 0x2e, 0x26, 0x53, 0x24, 0x68, 0x1e, 0x0e, 0x58, 0x3a,
193 0x19, 0x70, 0x3d, 0x02, 0x41, 0x0e, 0x38, 0x05, 0x76, 0x65, 0x4f, 0x31, 0x6c, 0x5e, 0x17, 0x04, 0x15, 0x36, 0x26, 0x64, 0x34, 0x14, 193 0x59, 0x50, 0x56, 0x37, 0x5f, 0x66, 0x01, 0x4c, 0x5a, 0x64, 0x32, 0x50, 0x7b, 0x6a, 0x20,
194 0x17, 0x7c, 0x0e, 0x0b, 0x5b, 0x55, 0x53, 0x6b, 0x00, 0x42, 0x41, 0x4f, 0x02, 0x5c, 0x13, 0x0a, 0x2c, 0x2c, 0x3e, 0x10, 0x14, 0x33, 194 0x72, 0x2b, 0x1d, 0x7e, 0x43, 0x7b, 0x61, 0x42, 0x0b, 0x61, 0x73, 0x24, 0x79, 0x3a, 0x6b,
195 0x45, 0x7c, 0x7a, 0x5a, 0x31, 0x61, 0x39, 0x08, 0x22, 0x6a, 0x1e, 0x0f, 0x6f, 0x1b, 0x6c, 0x13, 0x5e, 0x79, 0x20, 0x79, 0x50, 0x62, 195 0x4a, 0x79, 0x6e, 0x09, 0x0f, 0x27, 0x2d, 0x0c, 0x5e, 0x32, 0x4b, 0x0d, 0x79, 0x46, 0x39,
196 0x06, 0x2c, 0x76, 0x17, 0x04, 0x2b, 0x2a, 0x75, 0x1f, 0x0c, 0x37, 0x4e, 0x0f, 0x7b, 0x2d, 0x34, 0x75, 0x60, 0x31, 0x74, 0x2e, 0x0a, 196 0x21, 0x0a, 0x26, 0x5f, 0x3a, 0x00, 0x26, 0x3f, 0x13, 0x2e, 0x7e, 0x50, 0x2b, 0x67, 0x46,
197 0x4a, 0x11, 0x6c, 0x49, 0x25, 0x01, 0x3a, 0x3d, 0x22, 0x1e, 0x6d, 0x18, 0x51, 0x78, 0x2d, 0x62, 0x31, 0x4c, 0x50, 0x40, 0x17, 0x4b, 197 0x72, 0x3f, 0x3b, 0x01, 0x46, 0x1b, 0x0b, 0x35, 0x49, 0x39, 0x19, 0x70, 0x3d, 0x02, 0x41,
198 0x6f, 0x22, 0x00, 0x7f, 0x61, 0x2a, 0x34, 0x3e, 0x00, 0x5f, 0x2f, 0x5f, 0x2f, 0x14, 0x2a, 0x55, 0x27, 0x1f, 0x46, 0x1f, 0x12, 0x46, 198 0x0e, 0x38, 0x05, 0x76, 0x65, 0x4f, 0x31, 0x6c, 0x5e, 0x17, 0x04, 0x15, 0x36, 0x26, 0x64,
199 0x5e, 0x1e, 0x0c, 0x7c, 0x38, 0x01, 0x61, 0x64, 0x76, 0x22, 0x6e, 0x08, 0x20, 0x38, 0x4f, 0x73, 0x72, 0x55, 0x12, 0x42, 0x19, 0x50, 199 0x34, 0x14, 0x17, 0x7c, 0x0e, 0x0b, 0x5b, 0x55, 0x53, 0x6b, 0x00, 0x42, 0x41, 0x4f, 0x02,
200 0x61, 0x43, 0x77, 0x7d, 0x41, 0x2e, 0x35, 0x4f, 0x3d, 0x31, 0x28, 0x58, 0x67, 0x1b, 0x03, 0x51, 0x20, 0x32, 0x1c, 0x08, 0x6e, 0x37, 200 0x5c, 0x13, 0x0a, 0x2c, 0x2c, 0x3e, 0x10, 0x14, 0x33, 0x45, 0x7c, 0x7a, 0x5a, 0x31, 0x61,
201 0x75, 0x37, 0x44, 0x4f, 0x68, 0x19, 0x07, 0x64, 0x14, 0x28, 0x25, 0x2b, 0x69, 0x35, 0x18, 0x27, 0x26, 0x14, 0x13, 0x70, 0x42, 0x19, 201 0x39, 0x08, 0x22, 0x6a, 0x1e, 0x0f, 0x6f, 0x1b, 0x6c, 0x13, 0x5e, 0x79, 0x20, 0x79, 0x50,
202 0x12, 0x75, 0x3e, 0x02, 0x5d, 0x7c, 0x13, 0x1f, 0x16, 0x53, 0x3b, 0x74, 0x48, 0x3c, 0x5e, 0x39, 0x6c, 0x1c, 0x1c, 0x74, 0x39, 0x1f, 202 0x62, 0x06, 0x2c, 0x76, 0x17, 0x04, 0x2b, 0x2a, 0x75, 0x1f, 0x0c, 0x37, 0x4e, 0x0f, 0x7b,
203 0x00, 0x1b, 0x06, 0x0a, 0x68, 0x3b, 0x52, 0x4f, 0x1e, 0x6e, 0x3c, 0x35, 0x0c, 0x38, 0x0e, 0x0b, 0x3b, 0x1a, 0x76, 0x23, 0x29, 0x53, 203 0x2d, 0x34, 0x75, 0x60, 0x31, 0x74, 0x2e, 0x0a, 0x4a, 0x11, 0x6c, 0x49, 0x25, 0x01, 0x3a,
204 0x1e, 0x5f, 0x41, 0x0c, 0x4b, 0x0a, 0x65, 0x28, 0x78, 0x67, 0x48, 0x59, 0x26, 0x6d, 0x31, 0x76, 0x23, 0x70, 0x61, 0x64, 0x3b, 0x38, 204 0x3d, 0x22, 0x1e, 0x6d, 0x18, 0x51, 0x78, 0x2d, 0x62, 0x31, 0x4c, 0x50, 0x40, 0x17, 0x4b,
205 0x79, 0x66, 0x74, 0x53, 0x2c, 0x64, 0x64, 0x54, 0x03, 0x54, 0x65, 0x44, 0x4c, 0x18, 0x4f, 0x48, 0x20, 0x4f, 0x72, 0x10, 0x3f, 0x0c, 205 0x6f, 0x22, 0x00, 0x7f, 0x61, 0x2a, 0x34, 0x3e, 0x00, 0x5f, 0x2f, 0x5f, 0x2f, 0x14, 0x2a,
206 0x52, 0x2d, 0x03, 0x14, 0x03, 0x51, 0x42, 0x10, 0x77, 0x6a, 0x34, 0x06, 0x32, 0x03, 0x72, 0x14, 0x7c, 0x08, 0x5d, 0x52, 0x1a, 0x62, 206 0x55, 0x27, 0x1f, 0x46, 0x1f, 0x12, 0x46, 0x5e, 0x1e, 0x0c, 0x7c, 0x38, 0x01, 0x61, 0x64,
207 0x7c, 0x3e, 0x30, 0x7e, 0x5f, 0x7f, 0x54, 0x0f, 0x44, 0x49, 0x5d, 0x5e, 0x10, 0x6a, 0x06, 0x2b, 0x06, 0x53, 0x10, 0x39, 0x37, 0x32, 207 0x76, 0x22, 0x6e, 0x08, 0x20, 0x38, 0x4f, 0x73, 0x72, 0x55, 0x12, 0x42, 0x19, 0x50, 0x61,
208 0x4a, 0x4e, 0x3d, 0x2b, 0x65, 0x38, 0x39, 0x07, 0x72, 0x54, 0x64, 0x4d, 0x56, 0x6a, 0x03, 0x22, 0x70, 0x7b, 0x5f, 0x60, 0x0b, 0x2a, 208 0x43, 0x77, 0x7d, 0x41, 0x2e, 0x35, 0x4f, 0x3d, 0x31, 0x28, 0x58, 0x67, 0x1b, 0x03, 0x51,
209 0x0b, 0x6b, 0x10, 0x64, 0x14, 0x05, 0x22, 0x00, 0x73, 0x40, 0x23, 0x5b, 0x51, 0x1f, 0x2b, 0x1a, 0x5d, 0x69, 0x7a, 0x46, 0x0c, 0x5f, 209 0x20, 0x32, 0x1c, 0x08, 0x6e, 0x37, 0x75, 0x37, 0x44, 0x4f, 0x68, 0x19, 0x07, 0x64, 0x14,
210 0x32, 0x4b, 0x4a, 0x28, 0x52, 0x79, 0x5b, 0x12, 0x42, 0x18, 0x00, 0x5d, 0x27, 0x31, 0x53, 0x3c, 0x4c, 0x36, 0x4e, 0x38, 0x3f, 0x72, 210 0x28, 0x25, 0x2b, 0x69, 0x35, 0x18, 0x27, 0x26, 0x14, 0x13, 0x70, 0x42, 0x19, 0x12, 0x75,
211 0x03, 0x71, 0x02, 0x5b, 0x36, 0x59, 0x7f, 0x75, 0x6e, 0x08, 0x54, 0x0d, 0x34, 0x1c, 0x34, 0x57, 0x5d, 0x69, 0x48, 0x00, 0x3b, 0x05, 211 0x3e, 0x02, 0x5d, 0x7c, 0x13, 0x1f, 0x16, 0x53, 0x3b, 0x74, 0x48, 0x3c, 0x5e, 0x39, 0x6c,
212 0x07, 0x6e, 0x27, 0x65, 0x6e, 0x40, 0x3d, 0x3a, 0x4f, 0x72, 0x5d, 0x39, 0x16, 0x0f, 0x63, 0x12, 0x12, 0x15, 0x3a, 0x70, 0x0d, 0x57, 212 0x1c, 0x1c, 0x74, 0x39, 0x1f, 0x00, 0x1b, 0x06, 0x0a, 0x68, 0x3b, 0x52, 0x4f, 0x1e, 0x6e,
213 0x18, 0x0d, 0x5e, 0x3d, 0x22, 0x68, 0x68, 0x7c, 0x6d, 0x4f, 0x0c, 0x7b, 0x09, 0x2d, 0x4a, 0x73, 0x20, 0x47, 0x07, 0x57, 0x75, 0x5d, 213 0x3c, 0x35, 0x0c, 0x38, 0x0e, 0x0b, 0x3b, 0x1a, 0x76, 0x23, 0x29, 0x53, 0x1e, 0x5f, 0x41,
214 0x53, 0x70, 0x34, 0x21, 0x40, 0x57, 0x51, 0x5e, 0x49, 0x44, 0x00, 0x54, 0x27, 0x04, 0x68, 0x7e, 0x59, 0x56, 0x58, 0x74, 0x14, 0x3c, 214 0x0c, 0x4b, 0x0a, 0x65, 0x28, 0x78, 0x67, 0x48, 0x59, 0x26, 0x6d, 0x31, 0x76, 0x23, 0x70,
215 0x16, 0x33, 0x41, 0x16, 0x4b, 0x2f, 0x49, 0x37, 0x0a, 0x54, 0x08, 0x08, 0x1f, 0x39, 0x67, 0x76, 0x28, 0x28, 0x07, 0x1d, 0x61, 0x47, 215 0x61, 0x64, 0x3b, 0x38, 0x79, 0x66, 0x74, 0x53, 0x2c, 0x64, 0x64, 0x54, 0x03, 0x54, 0x65,
216 0x51, 0x4d, 0x75, 0x26, 0x52, 0x47, 0x47, 0x0c, 0x57, 0x58, 0x74, 0x3e, 0x62, 0x6c, 0x58, 0x3a, 0x44, 0x1e, 0x16, 0x2e, 0x21, 0x1c, 216 0x44, 0x4c, 0x18, 0x4f, 0x48, 0x20, 0x4f, 0x72, 0x10, 0x3f, 0x0c, 0x52, 0x2d, 0x03, 0x14,
217 0x73, 0x45, 0x67, 0x74, 0x4f, 0x33, 0x66, 0x0e, 0x74, 0x66, 0x26, 0x1f, 0x2e, 0x38, 0x44, 0x40, 0x7e, 0x2a, 0x50, 0x52, 0x5e, 0x43, 217 0x03, 0x51, 0x42, 0x10, 0x77, 0x6a, 0x34, 0x06, 0x32, 0x03, 0x72, 0x14, 0x7c, 0x08, 0x5d,
218 0x01, 0x7a, 0x38, 0x49, 0x3c, 0x55, 0x4d, 0x5a, 0x44, 0x08, 0x26, 0x59, 0x4d, 0x45, 0x0b, 0x48, 0x0a, 0x33, 0x5e, 0x4a, 0x4d, 0x75, 218 0x52, 0x1a, 0x62, 0x7c, 0x3e, 0x30, 0x7e, 0x5f, 0x7f, 0x54, 0x0f, 0x44, 0x49, 0x5d, 0x5e,
219 0x16, 0x17, 0x63, 0x46, 0x01, 0x2a, 0x55, 0x7b, 0x0f, 0x02, 0x73, 0x6a, 0x4b, 0x7f, 0x75, 0x65, 0x3c, 0x4c, 0x33, 0x39, 0x6c, 0x74, 219 0x10, 0x6a, 0x06, 0x2b, 0x06, 0x53, 0x10, 0x39, 0x37, 0x32, 0x4a, 0x4e, 0x3d, 0x2b, 0x65,
220 0x05, 0x60, 0x0f, 0x7f, 0x2d, 0x41, 0x4d, 0x4d, 0x46, 0x71, 0x09, 0x6f, 0x4f, 0x60, 0x15, 0x0f, 0x46, 0x73, 0x63, 0x4c, 0x5e, 0x74, 220 0x38, 0x39, 0x07, 0x72, 0x54, 0x64, 0x4d, 0x56, 0x6a, 0x03, 0x22, 0x70, 0x7b, 0x5f, 0x60,
221 0x30, 0x0d, 0x28, 0x43, 0x08, 0x72, 0x32, 0x04, 0x2e, 0x31, 0x29, 0x27, 0x44, 0x6d, 0x13, 0x17, 0x48, 0x0f, 0x49, 0x52, 0x10, 0x13, 221 0x0b, 0x2a, 0x0b, 0x6b, 0x10, 0x64, 0x14, 0x05, 0x22, 0x00, 0x73, 0x40, 0x23, 0x5b, 0x51,
222 0x7f, 0x17, 0x16, 0x62, 0x79, 0x35, 0x78, 0x3e, 0x01, 0x7c, 0x2e, 0x0f, 0x76, 0x3e, 0x5e, 0x53, 0x6c, 0x5b, 0x5f, 0x7c, 0x19, 0x41, 222 0x1f, 0x2b, 0x1a, 0x5d, 0x69, 0x7a, 0x46, 0x0c, 0x5f, 0x32, 0x4b, 0x4a, 0x28, 0x52, 0x79,
223 0x02, 0x2f, 0x17, 0x64, 0x41, 0x75, 0x10, 0x04, 0x47, 0x7c, 0x3d, 0x4b, 0x52, 0x00, 0x10, 0x5d, 0x51, 0x4e, 0x7a, 0x27, 0x25, 0x55, 223 0x5b, 0x12, 0x42, 0x18, 0x00, 0x5d, 0x27, 0x31, 0x53, 0x3c, 0x4c, 0x36, 0x4e, 0x38, 0x3f,
224 0x40, 0x12, 0x35, 0x60, 0x05, 0x1b, 0x34, 0x2d, 0x04, 0x7a, 0x6a, 0x69, 0x02, 0x79, 0x03, 0x3a, 0x2f, 0x06, 0x0a, 0x79, 0x7b, 0x12, 224 0x72, 0x03, 0x71, 0x02, 0x5b, 0x36, 0x59, 0x7f, 0x75, 0x6e, 0x08, 0x54, 0x0d, 0x34, 0x1c,
225 0x5d, 0x7c, 0x52, 0x29, 0x47, 0x58, 0x12, 0x73, 0x3f, 0x27, 0x56, 0x05, 0x0c, 0x48, 0x32, 0x58, 0x6b, 0x57, 0x5c, 0x03, 0x64, 0x56, 225 0x34, 0x57, 0x5d, 0x69, 0x48, 0x00, 0x3b, 0x05, 0x07, 0x6e, 0x27, 0x65, 0x6e, 0x40, 0x3d,
226 0x11, 0x52, 0x7a, 0x30, 0x36, 0x29, 0x17, 0x3b, 0x68, 0x7a, 0x7c, 0x05, 0x6b, 0x6b, 0x13, 0x6a, 0x24, 0x5c, 0x68, 0x42, 0x18, 0x32, 226 0x3a, 0x4f, 0x72, 0x5d, 0x39, 0x16, 0x0f, 0x63, 0x12, 0x12, 0x15, 0x3a, 0x70, 0x0d, 0x57,
227 0x03, 0x73, 0x6e, 0x04, 0x21, 0x2e, 0x01, 0x04, 0x63, 0x7d, 0x44, 0x41, 0x12, 0x31, 0x0b, 0x15, 0x1f, 0x70, 0x00, 0x2e, 0x66, 0x14, 227 0x18, 0x0d, 0x5e, 0x3d, 0x22, 0x68, 0x68, 0x7c, 0x6d, 0x4f, 0x0c, 0x7b, 0x09, 0x2d, 0x4a,
228 0x3c, 0x7f, 0x2b, 0x00, 0x1f, 0x0c, 0x28, 0x59, 0x0a, 0x16, 0x49, 0x5a, 0x5c, 0x64, 0x65, 0x4b, 0x11, 0x29, 0x15, 0x36, 0x5a, 0x65, 228 0x73, 0x20, 0x47, 0x07, 0x57, 0x75, 0x5d, 0x53, 0x70, 0x34, 0x21, 0x40, 0x57, 0x51, 0x5e,
229 0x19, 0x4f, 0x60, 0x23, 0x3a, 0x3a, 0x13, 0x25, 0x02, 0x78, 0x4c, 0x54}; 229 0x49, 0x44, 0x00, 0x54, 0x27, 0x04, 0x68, 0x7e, 0x59, 0x56, 0x58, 0x74, 0x14, 0x3c, 0x16,
230 0x33, 0x41, 0x16, 0x4b, 0x2f, 0x49, 0x37, 0x0a, 0x54, 0x08, 0x08, 0x1f, 0x39, 0x67, 0x76,
231 0x28, 0x28, 0x07, 0x1d, 0x61, 0x47, 0x51, 0x4d, 0x75, 0x26, 0x52, 0x47, 0x47, 0x0c, 0x57,
232 0x58, 0x74, 0x3e, 0x62, 0x6c, 0x58, 0x3a, 0x44, 0x1e, 0x16, 0x2e, 0x21, 0x1c, 0x73, 0x45,
233 0x67, 0x74, 0x4f, 0x33, 0x66, 0x0e, 0x74, 0x66, 0x26, 0x1f, 0x2e, 0x38, 0x44, 0x40, 0x7e,
234 0x2a, 0x50, 0x52, 0x5e, 0x43, 0x01, 0x7a, 0x38, 0x49, 0x3c, 0x55, 0x4d, 0x5a, 0x44, 0x08,
235 0x26, 0x59, 0x4d, 0x45, 0x0b, 0x48, 0x0a, 0x33, 0x5e, 0x4a, 0x4d, 0x75, 0x16, 0x17, 0x63,
236 0x46, 0x01, 0x2a, 0x55, 0x7b, 0x0f, 0x02, 0x73, 0x6a, 0x4b, 0x7f, 0x75, 0x65, 0x3c, 0x4c,
237 0x33, 0x39, 0x6c, 0x74, 0x05, 0x60, 0x0f, 0x7f, 0x2d, 0x41, 0x4d, 0x4d, 0x46, 0x71, 0x09,
238 0x6f, 0x4f, 0x60, 0x15, 0x0f, 0x46, 0x73, 0x63, 0x4c, 0x5e, 0x74, 0x30, 0x0d, 0x28, 0x43,
239 0x08, 0x72, 0x32, 0x04, 0x2e, 0x31, 0x29, 0x27, 0x44, 0x6d, 0x13, 0x17, 0x48, 0x0f, 0x49,
240 0x52, 0x10, 0x13, 0x7f, 0x17, 0x16, 0x62, 0x79, 0x35, 0x78, 0x3e, 0x01, 0x7c, 0x2e, 0x0f,
241 0x76, 0x3e, 0x5e, 0x53, 0x6c, 0x5b, 0x5f, 0x7c, 0x19, 0x41, 0x02, 0x2f, 0x17, 0x64, 0x41,
242 0x75, 0x10, 0x04, 0x47, 0x7c, 0x3d, 0x4b, 0x52, 0x00, 0x10, 0x5d, 0x51, 0x4e, 0x7a, 0x27,
243 0x25, 0x55, 0x40, 0x12, 0x35, 0x60, 0x05, 0x1b, 0x34, 0x2d, 0x04, 0x7a, 0x6a, 0x69, 0x02,
244 0x79, 0x03, 0x3a, 0x2f, 0x06, 0x0a, 0x79, 0x7b, 0x12, 0x5d, 0x7c, 0x52, 0x29, 0x47, 0x58,
245 0x12, 0x73, 0x3f, 0x27, 0x56, 0x05, 0x0c, 0x48, 0x32, 0x58, 0x6b, 0x57, 0x5c, 0x03, 0x64,
246 0x56, 0x11, 0x52, 0x7a, 0x30, 0x36, 0x29, 0x17, 0x3b, 0x68, 0x7a, 0x7c, 0x05, 0x6b, 0x6b,
247 0x13, 0x6a, 0x24, 0x5c, 0x68, 0x42, 0x18, 0x32, 0x03, 0x73, 0x6e, 0x04, 0x21, 0x2e, 0x01,
248 0x04, 0x63, 0x7d, 0x44, 0x41, 0x12, 0x31, 0x0b, 0x15, 0x1f, 0x70, 0x00, 0x2e, 0x66, 0x14,
249 0x3c, 0x7f, 0x2b, 0x00, 0x1f, 0x0c, 0x28, 0x59, 0x0a, 0x16, 0x49, 0x5a, 0x5c, 0x64, 0x65,
250 0x4b, 0x11, 0x29, 0x15, 0x36, 0x5a, 0x65, 0x19, 0x4f, 0x60, 0x23, 0x3a, 0x3a, 0x13, 0x25,
251 0x02, 0x78, 0x4c, 0x54};
230 char b64_known[1369] = { 252 char b64_known[1369] = {
231 0x43, 0x7a, 0x42, 0x45, 0x59, 0x6e, 0x77, 0x69, 0x48, 0x77, 0x30, 0x46, 0x5a, 0x79, 0x77, 0x71, 0x4f, 0x53, 0x46, 0x47, 0x43, 0x46, 253 0x43, 0x7a, 0x42, 0x45, 0x59, 0x6e, 0x77, 0x69, 0x48, 0x77, 0x30, 0x46, 0x5a, 0x79, 0x77,
232 0x42, 0x6d, 0x4e, 0x44, 0x63, 0x4c, 0x52, 0x55, 0x73, 0x34, 0x4d, 0x67, 0x5a, 0x36, 0x50, 0x6e, 0x38, 0x4d, 0x51, 0x42, 0x68, 0x72, 254 0x71, 0x4f, 0x53, 0x46, 0x47, 0x43, 0x46, 0x42, 0x6d, 0x4e, 0x44, 0x63, 0x4c, 0x52, 0x55,
233 0x4c, 0x57, 0x42, 0x4d, 0x59, 0x41, 0x77, 0x6a, 0x51, 0x7a, 0x73, 0x2b, 0x47, 0x78, 0x59, 0x45, 0x52, 0x6c, 0x67, 0x2f, 0x51, 0x47, 255 0x73, 0x34, 0x4d, 0x67, 0x5a, 0x36, 0x50, 0x6e, 0x38, 0x4d, 0x51, 0x42, 0x68, 0x72, 0x4c,
234 0x6f, 0x52, 0x42, 0x57, 0x4e, 0x78, 0x46, 0x44, 0x56, 0x48, 0x65, 0x52, 0x4e, 0x76, 0x61, 0x79, 0x63, 0x59, 0x57, 0x30, 0x67, 0x6e, 256 0x57, 0x42, 0x4d, 0x59, 0x41, 0x77, 0x6a, 0x51, 0x7a, 0x73, 0x2b, 0x47, 0x78, 0x59, 0x45,
235 0x50, 0x6d, 0x38, 0x56, 0x4d, 0x30, 0x38, 0x2b, 0x58, 0x6c, 0x46, 0x7a, 0x61, 0x43, 0x55, 0x50, 0x42, 0x6c, 0x74, 0x38, 0x63, 0x6e, 257 0x52, 0x6c, 0x67, 0x2f, 0x51, 0x47, 0x6f, 0x52, 0x42, 0x57, 0x4e, 0x78, 0x46, 0x44, 0x56,
236 0x55, 0x2b, 0x50, 0x78, 0x74, 0x63, 0x62, 0x57, 0x6f, 0x35, 0x66, 0x47, 0x4e, 0x6a, 0x59, 0x47, 0x78, 0x36, 0x4d, 0x33, 0x5a, 0x53, 258 0x48, 0x65, 0x52, 0x4e, 0x76, 0x61, 0x79, 0x63, 0x59, 0x57, 0x30, 0x67, 0x6e, 0x50, 0x6d,
237 0x45, 0x79, 0x55, 0x7a, 0x66, 0x57, 0x55, 0x6a, 0x4a, 0x78, 0x45, 0x47, 0x42, 0x6b, 0x64, 0x78, 0x48, 0x68, 0x52, 0x30, 0x59, 0x33, 259 0x38, 0x56, 0x4d, 0x30, 0x38, 0x2b, 0x58, 0x6c, 0x46, 0x7a, 0x61, 0x43, 0x55, 0x50, 0x42,
238 0x41, 0x74, 0x46, 0x53, 0x63, 0x59, 0x55, 0x51, 0x59, 0x46, 0x4d, 0x78, 0x45, 0x73, 0x61, 0x77, 0x41, 0x74, 0x64, 0x79, 0x42, 0x49, 260 0x6c, 0x74, 0x38, 0x63, 0x6e, 0x55, 0x2b, 0x50, 0x78, 0x74, 0x63, 0x62, 0x57, 0x6f, 0x35,
239 0x44, 0x58, 0x4e, 0x52, 0x52, 0x53, 0x56, 0x2f, 0x66, 0x7a, 0x55, 0x6d, 0x4c, 0x69, 0x5a, 0x54, 0x4a, 0x47, 0x67, 0x65, 0x44, 0x6c, 261 0x66, 0x47, 0x4e, 0x6a, 0x59, 0x47, 0x78, 0x36, 0x4d, 0x33, 0x5a, 0x53, 0x45, 0x79, 0x55,
240 0x67, 0x36, 0x57, 0x56, 0x42, 0x57, 0x4e, 0x31, 0x39, 0x6d, 0x41, 0x55, 0x78, 0x61, 0x5a, 0x44, 0x4a, 0x51, 0x65, 0x32, 0x6f, 0x67, 262 0x7a, 0x66, 0x57, 0x55, 0x6a, 0x4a, 0x78, 0x45, 0x47, 0x42, 0x6b, 0x64, 0x78, 0x48, 0x68,
241 0x63, 0x69, 0x73, 0x64, 0x66, 0x6b, 0x4e, 0x37, 0x59, 0x55, 0x49, 0x4c, 0x59, 0x58, 0x4d, 0x6b, 0x65, 0x54, 0x70, 0x72, 0x53, 0x6e, 263 0x52, 0x30, 0x59, 0x33, 0x41, 0x74, 0x46, 0x53, 0x63, 0x59, 0x55, 0x51, 0x59, 0x46, 0x4d,
242 0x6c, 0x75, 0x43, 0x51, 0x38, 0x6e, 0x4c, 0x51, 0x78, 0x65, 0x4d, 0x6b, 0x73, 0x4e, 0x65, 0x55, 0x59, 0x35, 0x49, 0x51, 0x6f, 0x6d, 264 0x78, 0x45, 0x73, 0x61, 0x77, 0x41, 0x74, 0x64, 0x79, 0x42, 0x49, 0x44, 0x58, 0x4e, 0x52,
243 0x58, 0x7a, 0x6f, 0x41, 0x4a, 0x6a, 0x38, 0x54, 0x4c, 0x6e, 0x35, 0x51, 0x4b, 0x32, 0x64, 0x47, 0x63, 0x6a, 0x38, 0x37, 0x41, 0x55, 265 0x52, 0x53, 0x56, 0x2f, 0x66, 0x7a, 0x55, 0x6d, 0x4c, 0x69, 0x5a, 0x54, 0x4a, 0x47, 0x67,
244 0x59, 0x62, 0x43, 0x7a, 0x56, 0x4a, 0x4f, 0x52, 0x6c, 0x77, 0x50, 0x51, 0x4a, 0x42, 0x44, 0x6a, 0x67, 0x46, 0x64, 0x6d, 0x56, 0x50, 266 0x65, 0x44, 0x6c, 0x67, 0x36, 0x57, 0x56, 0x42, 0x57, 0x4e, 0x31, 0x39, 0x6d, 0x41, 0x55,
245 0x4d, 0x57, 0x78, 0x65, 0x46, 0x77, 0x51, 0x56, 0x4e, 0x69, 0x5a, 0x6b, 0x4e, 0x42, 0x51, 0x58, 0x66, 0x41, 0x34, 0x4c, 0x57, 0x31, 267 0x78, 0x61, 0x5a, 0x44, 0x4a, 0x51, 0x65, 0x32, 0x6f, 0x67, 0x63, 0x69, 0x73, 0x64, 0x66,
246 0x56, 0x54, 0x61, 0x77, 0x42, 0x43, 0x51, 0x55, 0x38, 0x43, 0x58, 0x42, 0x4d, 0x4b, 0x4c, 0x43, 0x77, 0x2b, 0x45, 0x42, 0x51, 0x7a, 268 0x6b, 0x4e, 0x37, 0x59, 0x55, 0x49, 0x4c, 0x59, 0x58, 0x4d, 0x6b, 0x65, 0x54, 0x70, 0x72,
247 0x52, 0x58, 0x78, 0x36, 0x57, 0x6a, 0x46, 0x68, 0x4f, 0x51, 0x67, 0x69, 0x61, 0x68, 0x34, 0x50, 0x62, 0x78, 0x74, 0x73, 0x45, 0x31, 269 0x53, 0x6e, 0x6c, 0x75, 0x43, 0x51, 0x38, 0x6e, 0x4c, 0x51, 0x78, 0x65, 0x4d, 0x6b, 0x73,
248 0x35, 0x35, 0x49, 0x48, 0x6c, 0x51, 0x59, 0x67, 0x59, 0x73, 0x64, 0x68, 0x63, 0x45, 0x4b, 0x79, 0x70, 0x31, 0x48, 0x77, 0x77, 0x33, 270 0x4e, 0x65, 0x55, 0x59, 0x35, 0x49, 0x51, 0x6f, 0x6d, 0x58, 0x7a, 0x6f, 0x41, 0x4a, 0x6a,
249 0x54, 0x67, 0x39, 0x37, 0x4c, 0x54, 0x52, 0x31, 0x59, 0x44, 0x46, 0x30, 0x4c, 0x67, 0x70, 0x4b, 0x45, 0x57, 0x78, 0x4a, 0x4a, 0x51, 271 0x38, 0x54, 0x4c, 0x6e, 0x35, 0x51, 0x4b, 0x32, 0x64, 0x47, 0x63, 0x6a, 0x38, 0x37, 0x41,
250 0x45, 0x36, 0x50, 0x53, 0x49, 0x65, 0x62, 0x52, 0x68, 0x52, 0x65, 0x43, 0x31, 0x69, 0x4d, 0x55, 0x78, 0x51, 0x51, 0x42, 0x64, 0x4c, 272 0x55, 0x59, 0x62, 0x43, 0x7a, 0x56, 0x4a, 0x4f, 0x52, 0x6c, 0x77, 0x50, 0x51, 0x4a, 0x42,
251 0x62, 0x79, 0x49, 0x41, 0x66, 0x32, 0x45, 0x71, 0x4e, 0x44, 0x34, 0x41, 0x58, 0x79, 0x39, 0x66, 0x4c, 0x78, 0x51, 0x71, 0x56, 0x53, 273 0x44, 0x6a, 0x67, 0x46, 0x64, 0x6d, 0x56, 0x50, 0x4d, 0x57, 0x78, 0x65, 0x46, 0x77, 0x51,
252 0x63, 0x66, 0x52, 0x68, 0x38, 0x53, 0x52, 0x6c, 0x34, 0x65, 0x44, 0x48, 0x77, 0x34, 0x41, 0x57, 0x46, 0x6b, 0x64, 0x69, 0x4a, 0x75, 274 0x56, 0x4e, 0x69, 0x5a, 0x6b, 0x4e, 0x42, 0x51, 0x58, 0x66, 0x41, 0x34, 0x4c, 0x57, 0x31,
253 0x43, 0x43, 0x41, 0x34, 0x54, 0x33, 0x4e, 0x79, 0x56, 0x52, 0x4a, 0x43, 0x47, 0x56, 0x42, 0x68, 0x51, 0x33, 0x64, 0x39, 0x51, 0x53, 275 0x56, 0x54, 0x61, 0x77, 0x42, 0x43, 0x51, 0x55, 0x38, 0x43, 0x58, 0x42, 0x4d, 0x4b, 0x4c,
254 0x34, 0x31, 0x54, 0x7a, 0x30, 0x78, 0x4b, 0x46, 0x68, 0x6e, 0x47, 0x77, 0x4e, 0x52, 0x49, 0x44, 0x49, 0x63, 0x43, 0x47, 0x34, 0x33, 276 0x43, 0x77, 0x2b, 0x45, 0x42, 0x51, 0x7a, 0x52, 0x58, 0x78, 0x36, 0x57, 0x6a, 0x46, 0x68,
255 0x64, 0x54, 0x64, 0x45, 0x54, 0x32, 0x67, 0x5a, 0x42, 0x32, 0x51, 0x55, 0x4b, 0x43, 0x55, 0x72, 0x61, 0x54, 0x55, 0x59, 0x4a, 0x79, 277 0x4f, 0x51, 0x67, 0x69, 0x61, 0x68, 0x34, 0x50, 0x62, 0x78, 0x74, 0x73, 0x45, 0x31, 0x35,
256 0x59, 0x55, 0x45, 0x33, 0x42, 0x43, 0x47, 0x52, 0x4a, 0x31, 0x50, 0x67, 0x4a, 0x64, 0x66, 0x42, 0x4d, 0x66, 0x46, 0x6c, 0x4d, 0x37, 278 0x35, 0x49, 0x48, 0x6c, 0x51, 0x59, 0x67, 0x59, 0x73, 0x64, 0x68, 0x63, 0x45, 0x4b, 0x79,
257 0x64, 0x45, 0x67, 0x38, 0x58, 0x6a, 0x6c, 0x73, 0x48, 0x42, 0x78, 0x30, 0x4f, 0x52, 0x38, 0x41, 0x47, 0x77, 0x59, 0x4b, 0x61, 0x44, 279 0x70, 0x31, 0x48, 0x77, 0x77, 0x33, 0x54, 0x67, 0x39, 0x37, 0x4c, 0x54, 0x52, 0x31, 0x59,
258 0x74, 0x53, 0x54, 0x78, 0x35, 0x75, 0x50, 0x44, 0x55, 0x4d, 0x4f, 0x41, 0x34, 0x4c, 0x4f, 0x78, 0x70, 0x32, 0x49, 0x79, 0x6c, 0x54, 280 0x44, 0x46, 0x30, 0x4c, 0x67, 0x70, 0x4b, 0x45, 0x57, 0x78, 0x4a, 0x4a, 0x51, 0x45, 0x36,
259 0x48, 0x6c, 0x39, 0x42, 0x44, 0x45, 0x73, 0x4b, 0x5a, 0x53, 0x68, 0x34, 0x5a, 0x30, 0x68, 0x5a, 0x4a, 0x6d, 0x30, 0x78, 0x64, 0x69, 281 0x50, 0x53, 0x49, 0x65, 0x62, 0x52, 0x68, 0x52, 0x65, 0x43, 0x31, 0x69, 0x4d, 0x55, 0x78,
260 0x4e, 0x77, 0x59, 0x57, 0x51, 0x37, 0x4f, 0x48, 0x6c, 0x6d, 0x64, 0x46, 0x4d, 0x73, 0x5a, 0x47, 0x52, 0x55, 0x41, 0x31, 0x52, 0x6c, 282 0x51, 0x51, 0x42, 0x64, 0x4c, 0x62, 0x79, 0x49, 0x41, 0x66, 0x32, 0x45, 0x71, 0x4e, 0x44,
261 0x52, 0x45, 0x77, 0x59, 0x54, 0x30, 0x67, 0x67, 0x54, 0x33, 0x49, 0x51, 0x50, 0x77, 0x78, 0x53, 0x4c, 0x51, 0x4d, 0x55, 0x41, 0x31, 283 0x34, 0x41, 0x58, 0x79, 0x39, 0x66, 0x4c, 0x78, 0x51, 0x71, 0x56, 0x53, 0x63, 0x66, 0x52,
262 0x46, 0x43, 0x45, 0x48, 0x64, 0x71, 0x4e, 0x41, 0x59, 0x79, 0x41, 0x33, 0x49, 0x55, 0x66, 0x41, 0x68, 0x64, 0x55, 0x68, 0x70, 0x69, 284 0x68, 0x38, 0x53, 0x52, 0x6c, 0x34, 0x65, 0x44, 0x48, 0x77, 0x34, 0x41, 0x57, 0x46, 0x6b,
263 0x66, 0x44, 0x34, 0x77, 0x66, 0x6c, 0x39, 0x2f, 0x56, 0x41, 0x39, 0x45, 0x53, 0x56, 0x31, 0x65, 0x45, 0x47, 0x6f, 0x47, 0x4b, 0x77, 285 0x64, 0x69, 0x4a, 0x75, 0x43, 0x43, 0x41, 0x34, 0x54, 0x33, 0x4e, 0x79, 0x56, 0x52, 0x4a,
264 0x5a, 0x54, 0x45, 0x44, 0x6b, 0x33, 0x4d, 0x6b, 0x70, 0x4f, 0x50, 0x53, 0x74, 0x6c, 0x4f, 0x44, 0x6b, 0x48, 0x63, 0x6c, 0x52, 0x6b, 286 0x43, 0x47, 0x56, 0x42, 0x68, 0x51, 0x33, 0x64, 0x39, 0x51, 0x53, 0x34, 0x31, 0x54, 0x7a,
265 0x54, 0x56, 0x5a, 0x71, 0x41, 0x79, 0x4a, 0x77, 0x65, 0x31, 0x39, 0x67, 0x43, 0x79, 0x6f, 0x4c, 0x61, 0x78, 0x42, 0x6b, 0x46, 0x41, 287 0x30, 0x78, 0x4b, 0x46, 0x68, 0x6e, 0x47, 0x77, 0x4e, 0x52, 0x49, 0x44, 0x49, 0x63, 0x43,
266 0x55, 0x69, 0x41, 0x48, 0x4e, 0x41, 0x49, 0x31, 0x74, 0x52, 0x48, 0x79, 0x73, 0x61, 0x58, 0x57, 0x6c, 0x36, 0x52, 0x67, 0x78, 0x66, 288 0x47, 0x34, 0x33, 0x64, 0x54, 0x64, 0x45, 0x54, 0x32, 0x67, 0x5a, 0x42, 0x32, 0x51, 0x55,
267 0x4d, 0x6b, 0x74, 0x4b, 0x4b, 0x46, 0x4a, 0x35, 0x57, 0x78, 0x4a, 0x43, 0x47, 0x41, 0x42, 0x64, 0x4a, 0x7a, 0x46, 0x54, 0x50, 0x45, 289 0x4b, 0x43, 0x55, 0x72, 0x61, 0x54, 0x55, 0x59, 0x4a, 0x79, 0x59, 0x55, 0x45, 0x33, 0x42,
268 0x77, 0x32, 0x54, 0x6a, 0x67, 0x2f, 0x63, 0x67, 0x4e, 0x78, 0x41, 0x6c, 0x73, 0x32, 0x57, 0x58, 0x39, 0x31, 0x62, 0x67, 0x68, 0x55, 290 0x43, 0x47, 0x52, 0x4a, 0x31, 0x50, 0x67, 0x4a, 0x64, 0x66, 0x42, 0x4d, 0x66, 0x46, 0x6c,
269 0x44, 0x54, 0x51, 0x63, 0x4e, 0x46, 0x64, 0x64, 0x61, 0x55, 0x67, 0x41, 0x4f, 0x77, 0x55, 0x48, 0x62, 0x69, 0x64, 0x6c, 0x62, 0x6b, 291 0x4d, 0x37, 0x64, 0x45, 0x67, 0x38, 0x58, 0x6a, 0x6c, 0x73, 0x48, 0x42, 0x78, 0x30, 0x4f,
270 0x41, 0x39, 0x4f, 0x6b, 0x39, 0x79, 0x58, 0x54, 0x6b, 0x57, 0x44, 0x32, 0x4d, 0x53, 0x45, 0x68, 0x55, 0x36, 0x63, 0x41, 0x31, 0x58, 292 0x52, 0x38, 0x41, 0x47, 0x77, 0x59, 0x4b, 0x61, 0x44, 0x74, 0x53, 0x54, 0x78, 0x35, 0x75,
271 0x47, 0x41, 0x31, 0x65, 0x50, 0x53, 0x4a, 0x6f, 0x61, 0x48, 0x78, 0x74, 0x54, 0x77, 0x78, 0x37, 0x43, 0x53, 0x31, 0x4b, 0x63, 0x79, 293 0x50, 0x44, 0x55, 0x4d, 0x4f, 0x41, 0x34, 0x4c, 0x4f, 0x78, 0x70, 0x32, 0x49, 0x79, 0x6c,
272 0x42, 0x48, 0x42, 0x31, 0x64, 0x31, 0x58, 0x56, 0x4e, 0x77, 0x4e, 0x43, 0x46, 0x41, 0x56, 0x31, 0x46, 0x65, 0x53, 0x55, 0x51, 0x41, 294 0x54, 0x48, 0x6c, 0x39, 0x42, 0x44, 0x45, 0x73, 0x4b, 0x5a, 0x53, 0x68, 0x34, 0x5a, 0x30,
273 0x56, 0x43, 0x63, 0x45, 0x61, 0x48, 0x35, 0x5a, 0x56, 0x6c, 0x68, 0x30, 0x46, 0x44, 0x77, 0x57, 0x4d, 0x30, 0x45, 0x57, 0x53, 0x79, 295 0x68, 0x5a, 0x4a, 0x6d, 0x30, 0x78, 0x64, 0x69, 0x4e, 0x77, 0x59, 0x57, 0x51, 0x37, 0x4f,
274 0x39, 0x4a, 0x4e, 0x77, 0x70, 0x55, 0x43, 0x41, 0x67, 0x66, 0x4f, 0x57, 0x64, 0x32, 0x4b, 0x43, 0x67, 0x48, 0x48, 0x57, 0x46, 0x48, 296 0x48, 0x6c, 0x6d, 0x64, 0x46, 0x4d, 0x73, 0x5a, 0x47, 0x52, 0x55, 0x41, 0x31, 0x52, 0x6c,
275 0x55, 0x55, 0x31, 0x31, 0x4a, 0x6c, 0x4a, 0x48, 0x52, 0x77, 0x78, 0x58, 0x57, 0x48, 0x51, 0x2b, 0x59, 0x6d, 0x78, 0x59, 0x4f, 0x6b, 297 0x52, 0x45, 0x77, 0x59, 0x54, 0x30, 0x67, 0x67, 0x54, 0x33, 0x49, 0x51, 0x50, 0x77, 0x78,
276 0x51, 0x65, 0x46, 0x69, 0x34, 0x68, 0x48, 0x48, 0x4e, 0x46, 0x5a, 0x33, 0x52, 0x50, 0x4d, 0x32, 0x59, 0x4f, 0x64, 0x47, 0x59, 0x6d, 298 0x53, 0x4c, 0x51, 0x4d, 0x55, 0x41, 0x31, 0x46, 0x43, 0x45, 0x48, 0x64, 0x71, 0x4e, 0x41,
277 0x48, 0x79, 0x34, 0x34, 0x52, 0x45, 0x42, 0x2b, 0x4b, 0x6c, 0x42, 0x53, 0x58, 0x6b, 0x4d, 0x42, 0x65, 0x6a, 0x68, 0x4a, 0x50, 0x46, 299 0x59, 0x79, 0x41, 0x33, 0x49, 0x55, 0x66, 0x41, 0x68, 0x64, 0x55, 0x68, 0x70, 0x69, 0x66,
278 0x56, 0x4e, 0x57, 0x6b, 0x51, 0x49, 0x4a, 0x6c, 0x6c, 0x4e, 0x52, 0x51, 0x74, 0x49, 0x43, 0x6a, 0x4e, 0x65, 0x53, 0x6b, 0x31, 0x31, 300 0x44, 0x34, 0x77, 0x66, 0x6c, 0x39, 0x2f, 0x56, 0x41, 0x39, 0x45, 0x53, 0x56, 0x31, 0x65,
279 0x46, 0x68, 0x64, 0x6a, 0x52, 0x67, 0x45, 0x71, 0x56, 0x58, 0x73, 0x50, 0x41, 0x6e, 0x4e, 0x71, 0x53, 0x33, 0x39, 0x31, 0x5a, 0x54, 301 0x45, 0x47, 0x6f, 0x47, 0x4b, 0x77, 0x5a, 0x54, 0x45, 0x44, 0x6b, 0x33, 0x4d, 0x6b, 0x70,
280 0x78, 0x4d, 0x4d, 0x7a, 0x6c, 0x73, 0x64, 0x41, 0x56, 0x67, 0x44, 0x33, 0x38, 0x74, 0x51, 0x55, 0x31, 0x4e, 0x52, 0x6e, 0x45, 0x4a, 302 0x4f, 0x50, 0x53, 0x74, 0x6c, 0x4f, 0x44, 0x6b, 0x48, 0x63, 0x6c, 0x52, 0x6b, 0x54, 0x56,
281 0x62, 0x30, 0x39, 0x67, 0x46, 0x51, 0x39, 0x47, 0x63, 0x32, 0x4e, 0x4d, 0x58, 0x6e, 0x51, 0x77, 0x44, 0x53, 0x68, 0x44, 0x43, 0x48, 303 0x5a, 0x71, 0x41, 0x79, 0x4a, 0x77, 0x65, 0x31, 0x39, 0x67, 0x43, 0x79, 0x6f, 0x4c, 0x61,
282 0x49, 0x79, 0x42, 0x43, 0x34, 0x78, 0x4b, 0x53, 0x64, 0x45, 0x62, 0x52, 0x4d, 0x58, 0x53, 0x41, 0x39, 0x4a, 0x55, 0x68, 0x41, 0x54, 304 0x78, 0x42, 0x6b, 0x46, 0x41, 0x55, 0x69, 0x41, 0x48, 0x4e, 0x41, 0x49, 0x31, 0x74, 0x52,
283 0x66, 0x78, 0x63, 0x57, 0x59, 0x6e, 0x6b, 0x31, 0x65, 0x44, 0x34, 0x42, 0x66, 0x43, 0x34, 0x50, 0x64, 0x6a, 0x35, 0x65, 0x55, 0x32, 305 0x48, 0x79, 0x73, 0x61, 0x58, 0x57, 0x6c, 0x36, 0x52, 0x67, 0x78, 0x66, 0x4d, 0x6b, 0x74,
284 0x78, 0x62, 0x58, 0x33, 0x77, 0x5a, 0x51, 0x51, 0x49, 0x76, 0x46, 0x32, 0x52, 0x42, 0x64, 0x52, 0x41, 0x45, 0x52, 0x33, 0x77, 0x39, 306 0x4b, 0x4b, 0x46, 0x4a, 0x35, 0x57, 0x78, 0x4a, 0x43, 0x47, 0x41, 0x42, 0x64, 0x4a, 0x7a,
285 0x53, 0x31, 0x49, 0x41, 0x45, 0x46, 0x31, 0x52, 0x54, 0x6e, 0x6f, 0x6e, 0x4a, 0x56, 0x56, 0x41, 0x45, 0x6a, 0x56, 0x67, 0x42, 0x52, 307 0x46, 0x54, 0x50, 0x45, 0x77, 0x32, 0x54, 0x6a, 0x67, 0x2f, 0x63, 0x67, 0x4e, 0x78, 0x41,
286 0x73, 0x30, 0x4c, 0x51, 0x52, 0x36, 0x61, 0x6d, 0x6b, 0x43, 0x65, 0x51, 0x4d, 0x36, 0x4c, 0x77, 0x59, 0x4b, 0x65, 0x58, 0x73, 0x53, 308 0x6c, 0x73, 0x32, 0x57, 0x58, 0x39, 0x31, 0x62, 0x67, 0x68, 0x55, 0x44, 0x54, 0x51, 0x63,
287 0x58, 0x58, 0x78, 0x53, 0x4b, 0x55, 0x64, 0x59, 0x45, 0x6e, 0x4d, 0x2f, 0x4a, 0x31, 0x59, 0x46, 0x44, 0x45, 0x67, 0x79, 0x57, 0x47, 309 0x4e, 0x46, 0x64, 0x64, 0x61, 0x55, 0x67, 0x41, 0x4f, 0x77, 0x55, 0x48, 0x62, 0x69, 0x64,
288 0x74, 0x58, 0x58, 0x41, 0x4e, 0x6b, 0x56, 0x68, 0x46, 0x53, 0x65, 0x6a, 0x41, 0x32, 0x4b, 0x52, 0x63, 0x37, 0x61, 0x48, 0x70, 0x38, 310 0x6c, 0x62, 0x6b, 0x41, 0x39, 0x4f, 0x6b, 0x39, 0x79, 0x58, 0x54, 0x6b, 0x57, 0x44, 0x32,
289 0x42, 0x57, 0x74, 0x72, 0x45, 0x32, 0x6f, 0x6b, 0x58, 0x47, 0x68, 0x43, 0x47, 0x44, 0x49, 0x44, 0x63, 0x32, 0x34, 0x45, 0x49, 0x53, 311 0x4d, 0x53, 0x45, 0x68, 0x55, 0x36, 0x63, 0x41, 0x31, 0x58, 0x47, 0x41, 0x31, 0x65, 0x50,
290 0x34, 0x42, 0x42, 0x47, 0x4e, 0x39, 0x52, 0x45, 0x45, 0x53, 0x4d, 0x51, 0x73, 0x56, 0x48, 0x33, 0x41, 0x41, 0x4c, 0x6d, 0x59, 0x55, 312 0x53, 0x4a, 0x6f, 0x61, 0x48, 0x78, 0x74, 0x54, 0x77, 0x78, 0x37, 0x43, 0x53, 0x31, 0x4b,
291 0x50, 0x48, 0x38, 0x72, 0x41, 0x42, 0x38, 0x4d, 0x4b, 0x46, 0x6b, 0x4b, 0x46, 0x6b, 0x6c, 0x61, 0x58, 0x47, 0x52, 0x6c, 0x53, 0x78, 313 0x63, 0x79, 0x42, 0x48, 0x42, 0x31, 0x64, 0x31, 0x58, 0x56, 0x4e, 0x77, 0x4e, 0x43, 0x46,
292 0x45, 0x70, 0x46, 0x54, 0x5a, 0x61, 0x5a, 0x52, 0x6c, 0x50, 0x59, 0x43, 0x4d, 0x36, 0x4f, 0x68, 0x4d, 0x6c, 0x41, 0x6e, 0x68, 0x4d, 314 0x41, 0x56, 0x31, 0x46, 0x65, 0x53, 0x55, 0x51, 0x41, 0x56, 0x43, 0x63, 0x45, 0x61, 0x48,
293 0x56, 0x41, 0x3d, 0x3d, 0x00}; 315 0x35, 0x5a, 0x56, 0x6c, 0x68, 0x30, 0x46, 0x44, 0x77, 0x57, 0x4d, 0x30, 0x45, 0x57, 0x53,
316 0x79, 0x39, 0x4a, 0x4e, 0x77, 0x70, 0x55, 0x43, 0x41, 0x67, 0x66, 0x4f, 0x57, 0x64, 0x32,
317 0x4b, 0x43, 0x67, 0x48, 0x48, 0x57, 0x46, 0x48, 0x55, 0x55, 0x31, 0x31, 0x4a, 0x6c, 0x4a,
318 0x48, 0x52, 0x77, 0x78, 0x58, 0x57, 0x48, 0x51, 0x2b, 0x59, 0x6d, 0x78, 0x59, 0x4f, 0x6b,
319 0x51, 0x65, 0x46, 0x69, 0x34, 0x68, 0x48, 0x48, 0x4e, 0x46, 0x5a, 0x33, 0x52, 0x50, 0x4d,
320 0x32, 0x59, 0x4f, 0x64, 0x47, 0x59, 0x6d, 0x48, 0x79, 0x34, 0x34, 0x52, 0x45, 0x42, 0x2b,
321 0x4b, 0x6c, 0x42, 0x53, 0x58, 0x6b, 0x4d, 0x42, 0x65, 0x6a, 0x68, 0x4a, 0x50, 0x46, 0x56,
322 0x4e, 0x57, 0x6b, 0x51, 0x49, 0x4a, 0x6c, 0x6c, 0x4e, 0x52, 0x51, 0x74, 0x49, 0x43, 0x6a,
323 0x4e, 0x65, 0x53, 0x6b, 0x31, 0x31, 0x46, 0x68, 0x64, 0x6a, 0x52, 0x67, 0x45, 0x71, 0x56,
324 0x58, 0x73, 0x50, 0x41, 0x6e, 0x4e, 0x71, 0x53, 0x33, 0x39, 0x31, 0x5a, 0x54, 0x78, 0x4d,
325 0x4d, 0x7a, 0x6c, 0x73, 0x64, 0x41, 0x56, 0x67, 0x44, 0x33, 0x38, 0x74, 0x51, 0x55, 0x31,
326 0x4e, 0x52, 0x6e, 0x45, 0x4a, 0x62, 0x30, 0x39, 0x67, 0x46, 0x51, 0x39, 0x47, 0x63, 0x32,
327 0x4e, 0x4d, 0x58, 0x6e, 0x51, 0x77, 0x44, 0x53, 0x68, 0x44, 0x43, 0x48, 0x49, 0x79, 0x42,
328 0x43, 0x34, 0x78, 0x4b, 0x53, 0x64, 0x45, 0x62, 0x52, 0x4d, 0x58, 0x53, 0x41, 0x39, 0x4a,
329 0x55, 0x68, 0x41, 0x54, 0x66, 0x78, 0x63, 0x57, 0x59, 0x6e, 0x6b, 0x31, 0x65, 0x44, 0x34,
330 0x42, 0x66, 0x43, 0x34, 0x50, 0x64, 0x6a, 0x35, 0x65, 0x55, 0x32, 0x78, 0x62, 0x58, 0x33,
331 0x77, 0x5a, 0x51, 0x51, 0x49, 0x76, 0x46, 0x32, 0x52, 0x42, 0x64, 0x52, 0x41, 0x45, 0x52,
332 0x33, 0x77, 0x39, 0x53, 0x31, 0x49, 0x41, 0x45, 0x46, 0x31, 0x52, 0x54, 0x6e, 0x6f, 0x6e,
333 0x4a, 0x56, 0x56, 0x41, 0x45, 0x6a, 0x56, 0x67, 0x42, 0x52, 0x73, 0x30, 0x4c, 0x51, 0x52,
334 0x36, 0x61, 0x6d, 0x6b, 0x43, 0x65, 0x51, 0x4d, 0x36, 0x4c, 0x77, 0x59, 0x4b, 0x65, 0x58,
335 0x73, 0x53, 0x58, 0x58, 0x78, 0x53, 0x4b, 0x55, 0x64, 0x59, 0x45, 0x6e, 0x4d, 0x2f, 0x4a,
336 0x31, 0x59, 0x46, 0x44, 0x45, 0x67, 0x79, 0x57, 0x47, 0x74, 0x58, 0x58, 0x41, 0x4e, 0x6b,
337 0x56, 0x68, 0x46, 0x53, 0x65, 0x6a, 0x41, 0x32, 0x4b, 0x52, 0x63, 0x37, 0x61, 0x48, 0x70,
338 0x38, 0x42, 0x57, 0x74, 0x72, 0x45, 0x32, 0x6f, 0x6b, 0x58, 0x47, 0x68, 0x43, 0x47, 0x44,
339 0x49, 0x44, 0x63, 0x32, 0x34, 0x45, 0x49, 0x53, 0x34, 0x42, 0x42, 0x47, 0x4e, 0x39, 0x52,
340 0x45, 0x45, 0x53, 0x4d, 0x51, 0x73, 0x56, 0x48, 0x33, 0x41, 0x41, 0x4c, 0x6d, 0x59, 0x55,
341 0x50, 0x48, 0x38, 0x72, 0x41, 0x42, 0x38, 0x4d, 0x4b, 0x46, 0x6b, 0x4b, 0x46, 0x6b, 0x6c,
342 0x61, 0x58, 0x47, 0x52, 0x6c, 0x53, 0x78, 0x45, 0x70, 0x46, 0x54, 0x5a, 0x61, 0x5a, 0x52,
343 0x6c, 0x50, 0x59, 0x43, 0x4d, 0x36, 0x4f, 0x68, 0x4d, 0x6c, 0x41, 0x6e, 0x68, 0x4d, 0x56,
344 0x41, 0x3d, 0x3d, 0x00};
294 char *b64_test; 345 char *b64_test;
295 346
296 plan_tests(1); 347 plan_tests(1);
diff --git a/lib/tests/test_cmd.c b/lib/tests/test_cmd.c
index c8867dfb..d51016cc 100644
--- a/lib/tests/test_cmd.c
+++ b/lib/tests/test_cmd.c
@@ -38,36 +38,35 @@ char *get_command(char *const *line) {
38} 38}
39 39
40int main(int argc, char **argv) { 40int main(int argc, char **argv) {
41 char **command_line = malloc(sizeof(char *) * COMMAND_LINE);
42 char *command = NULL;
43 char *perl;
44 output chld_out, chld_err;
45 int c;
46 int result = UNSET;
47
48 plan_tests(51); 41 plan_tests(51);
49 42
50 diag("Running plain echo command, set one"); 43 diag("Running plain echo command, set one");
51 44
52 /* ensure everything is empty before we begin */ 45 /* ensure everything is empty before we begin */
46
47 output chld_out;
53 memset(&chld_out, 0, sizeof(output)); 48 memset(&chld_out, 0, sizeof(output));
49 output chld_err;
54 memset(&chld_err, 0, sizeof(output)); 50 memset(&chld_err, 0, sizeof(output));
55 ok(chld_out.lines == 0, "(initialised) Checking stdout is reset"); 51 ok(chld_out.lines == 0, "(initialised) Checking stdout is reset");
56 ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); 52 ok(chld_err.lines == 0, "(initialised) Checking stderr is reset");
53 int result = UNSET;
57 ok(result == UNSET, "(initialised) Checking exit code is reset"); 54 ok(result == UNSET, "(initialised) Checking exit code is reset");
58 55
56 char **command_line = malloc(sizeof(char *) * COMMAND_LINE);
59 command_line[0] = strdup("/bin/echo"); 57 command_line[0] = strdup("/bin/echo");
60 command_line[1] = strdup("this"); 58 command_line[1] = strdup("this");
61 command_line[2] = strdup("is"); 59 command_line[2] = strdup("is");
62 command_line[3] = strdup("test"); 60 command_line[3] = strdup("test");
63 command_line[4] = strdup("one"); 61 command_line[4] = strdup("one");
64 62
65 command = get_command(command_line); 63 char *command = get_command(command_line);
66 64
67 result = cmd_run_array(command_line, &chld_out, &chld_err, 0); 65 result = cmd_run_array(command_line, &chld_out, &chld_err, 0);
68 ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); 66 ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines");
69 ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); 67 ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines");
70 ok(strcmp(chld_out.line[0], "this is test one") == 0, "(array) Check for expected stdout output"); 68 ok(strcmp(chld_out.line[0], "this is test one") == 0,
69 "(array) Check for expected stdout output");
71 ok(result == 0, "(array) Checking exit code"); 70 ok(result == 0, "(array) Checking exit code");
72 71
73 /* ensure everything is empty again */ 72 /* ensure everything is empty again */
@@ -82,7 +81,8 @@ int main(int argc, char **argv) {
82 81
83 ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); 82 ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines");
84 ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); 83 ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines");
85 ok(strcmp(chld_out.line[0], "this is test one") == 0, "(string) Check for expected stdout output"); 84 ok(strcmp(chld_out.line[0], "this is test one") == 0,
85 "(string) Check for expected stdout output");
86 ok(result == 0, "(string) Checking exit code"); 86 ok(result == 0, "(string) Checking exit code");
87 87
88 diag("Running plain echo command, set two"); 88 diag("Running plain echo command, set two");
@@ -104,7 +104,8 @@ int main(int argc, char **argv) {
104 result = cmd_run_array(command_line, &chld_out, &chld_err, 0); 104 result = cmd_run_array(command_line, &chld_out, &chld_err, 0);
105 ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); 105 ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines");
106 ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); 106 ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines");
107 ok(strcmp(chld_out.line[0], "this is test two") == 0, "(array) Check for expected stdout output"); 107 ok(strcmp(chld_out.line[0], "this is test two") == 0,
108 "(array) Check for expected stdout output");
108 ok(result == 0, "(array) Checking exit code"); 109 ok(result == 0, "(array) Checking exit code");
109 110
110 /* ensure everything is empty again */ 111 /* ensure everything is empty again */
@@ -119,7 +120,8 @@ int main(int argc, char **argv) {
119 120
120 ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); 121 ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines");
121 ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); 122 ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines");
122 ok(strcmp(chld_out.line[0], "this is test one") == 0, "(string) Check for expected stdout output"); 123 ok(strcmp(chld_out.line[0], "this is test one") == 0,
124 "(string) Check for expected stdout output");
123 ok(result == 0, "(string) Checking exit code"); 125 ok(result == 0, "(string) Checking exit code");
124 126
125 /* ensure everything is empty again */ 127 /* ensure everything is empty again */
@@ -130,7 +132,8 @@ int main(int argc, char **argv) {
130 ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); 132 ok(chld_err.lines == 0, "(initialised) Checking stderr is reset");
131 ok(result == UNSET, "(initialised) Checking exit code is reset"); 133 ok(result == UNSET, "(initialised) Checking exit code is reset");
132 134
133 /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line output */ 135 /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line
136 * output */
134 command_line[0] = strdup("/bin/echo"); 137 command_line[0] = strdup("/bin/echo");
135 command_line[1] = strdup("this is a test via echo\nline two\nit's line 3"); 138 command_line[1] = strdup("this is a test via echo\nline two\nit's line 3");
136 command_line[2] = strdup("and (note space between '3' and 'and') $$ will not get evaluated"); 139 command_line[2] = strdup("and (note space between '3' and 'and') $$ will not get evaluated");
@@ -138,9 +141,12 @@ int main(int argc, char **argv) {
138 result = cmd_run_array(command_line, &chld_out, &chld_err, 0); 141 result = cmd_run_array(command_line, &chld_out, &chld_err, 0);
139 ok(chld_out.lines == 3, "(array) Check for expected number of stdout lines"); 142 ok(chld_out.lines == 3, "(array) Check for expected number of stdout lines");
140 ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); 143 ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines");
141 ok(strcmp(chld_out.line[0], "this is a test via echo") == 0, "(array) Check line 1 for expected stdout output"); 144 ok(strcmp(chld_out.line[0], "this is a test via echo") == 0,
142 ok(strcmp(chld_out.line[1], "line two") == 0, "(array) Check line 2 for expected stdout output"); 145 "(array) Check line 1 for expected stdout output");
143 ok(strcmp(chld_out.line[2], "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0, 146 ok(strcmp(chld_out.line[1], "line two") == 0,
147 "(array) Check line 2 for expected stdout output");
148 ok(strcmp(chld_out.line[2],
149 "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0,
144 "(array) Check line 3 for expected stdout output"); 150 "(array) Check line 3 for expected stdout output");
145 ok(result == 0, "(array) Checking exit code"); 151 ok(result == 0, "(array) Checking exit code");
146 152
@@ -171,7 +177,8 @@ int main(int argc, char **argv) {
171 177
172 ok(chld_out.lines == 0, "/bin/sh returns no stdout when file is missing..."); 178 ok(chld_out.lines == 0, "/bin/sh returns no stdout when file is missing...");
173 ok(chld_err.lines == 1, "...but does give an error line"); 179 ok(chld_err.lines == 1, "...but does give an error line");
174 ok(strstr(chld_err.line[0], "non-existent-file") != NULL, "And missing filename is in error message"); 180 ok(strstr(chld_err.line[0], "non-existent-file") != NULL,
181 "And missing filename is in error message");
175 ok(result != 0, "Get non-zero return code from /bin/sh"); 182 ok(result != 0, "Get non-zero return code from /bin/sh");
176 183
177 /* ensure everything is empty again */ 184 /* ensure everything is empty again */
diff --git a/lib/tests/test_disk.t b/lib/tests/test_disk.t
deleted file mode 100755
index da84dfdf..00000000
--- a/lib/tests/test_disk.t
+++ /dev/null
@@ -1,6 +0,0 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_disk") {
4 plan skip_all => "./test_disk not compiled - please enable libtap library to test";
5}
6exec "./test_disk";
diff --git a/lib/tests/test_generic_output.c b/lib/tests/test_generic_output.c
new file mode 100644
index 00000000..e4a78bcd
--- /dev/null
+++ b/lib/tests/test_generic_output.c
@@ -0,0 +1,317 @@
1#include "../lib/output.h"
2#include "../../tap/tap.h"
3#include "./states.h"
4
5#include <string.h>
6
7void test_one_subcheck(void);
8void test_two_subchecks(void);
9
10void test_perfdata_formatting(void);
11void test_perfdata_formatting2(void);
12
13void test_deep_check_hierarchy(void);
14void test_deep_check_hierarchy2(void);
15
16void test_default_states1(void);
17void test_default_states2(void);
18
19int main(void) {
20 plan_tests(19);
21
22 diag("Simple test with one subcheck");
23 test_one_subcheck();
24
25 diag("Test with two subchecks");
26 test_two_subchecks();
27
28 diag("Test for performance data formatting");
29 test_perfdata_formatting();
30
31 diag("Another test for performance data formatting");
32 test_perfdata_formatting2();
33
34 diag("Test for deeper hierarchies");
35 test_deep_check_hierarchy();
36
37 diag("Another test for deeper hierarchies");
38 test_deep_check_hierarchy2();
39
40 diag("Testing the default state logic");
41 test_default_states1();
42
43 diag("Testing the default state logic #2");
44 test_default_states2();
45
46 return exit_status();
47}
48
49void test_one_subcheck(void) {
50 mp_subcheck sc1 = mp_subcheck_init();
51
52 sc1.output = "foobar";
53 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
54
55 mp_check check = mp_check_init();
56 mp_add_subcheck_to_check(&check, sc1);
57
58 ok(mp_compute_check_state(check) == STATE_WARNING, "Main state should be warning");
59
60 char *output = mp_fmt_output(check);
61
62 // diag("Formatted output");
63 // diag(output);
64
65 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
66 "\t\\_[WARNING] - foobar\n";
67
68 // diag("Expected output");
69 // diag(expected);
70
71 ok(strcmp(output, expected) == 0, "Simple output test");
72}
73
74void test_perfdata_formatting2(void) {
75 mp_perfdata pd1 = perfdata_init();
76 mp_perfdata pd2 = perfdata_init();
77
78 pd1.label = "foo";
79 pd2.label = "bar";
80
81 pd1 = mp_set_pd_value(pd1, 23);
82 pd2 = mp_set_pd_value(pd2, 1LL);
83
84 pd_list *tmp = pd_list_init();
85
86 pd_list_append(tmp, pd1);
87 pd_list_append(tmp, pd2);
88
89 char *result = pd_list_to_string(*tmp);
90
91 ok(strcmp(result, "foo=23;;; bar=1;;;") == 0, "Perfdata string formatting");
92}
93
94void test_perfdata_formatting(void) {
95 mp_perfdata pd1 = perfdata_init();
96
97 pd1.uom = "s";
98 pd1.label = "foo";
99
100 pd1 = mp_set_pd_value(pd1, 23);
101
102 char *pd_string = pd_to_string(pd1);
103
104 ok(strcmp(pd_string, "foo=23s;;;") == 0, "Perfdata string formatting");
105}
106
107void test_two_subchecks(void) {
108 mp_subcheck sc1 = mp_subcheck_init();
109
110 sc1.output = "foobar";
111 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
112
113 ok(mp_compute_subcheck_state(sc1) == STATE_WARNING,
114 "Test subcheck state directly after setting it");
115
116 mp_perfdata pd1 = perfdata_init();
117
118 pd1 = mp_set_pd_value(pd1, 23);
119
120 pd1.uom = "s";
121 pd1.label = "foo";
122
123 mp_add_perfdata_to_subcheck(&sc1, pd1);
124
125 mp_subcheck sc2 = mp_subcheck_init();
126 sc2.output = "baz";
127 sc2 = mp_set_subcheck_state(sc2, STATE_OK);
128
129 ok(mp_compute_subcheck_state(sc2) == STATE_OK, "Test subcheck 2 state after setting it");
130
131 mp_add_subcheck_to_subcheck(&sc1, sc2);
132
133 ok(mp_compute_subcheck_state(sc1) == STATE_WARNING,
134 "Test subcheck state after adding a subcheck");
135
136 mp_check check = mp_check_init();
137 mp_add_subcheck_to_check(&check, sc1);
138
139 ok(mp_compute_check_state(check) == STATE_WARNING, "Test main check result");
140
141 char *output = mp_fmt_output(check);
142
143 // diag("Formatted output. Length: %u", strlen(output));
144 // diag(output);
145
146 ok(output != NULL, "Output should not be NULL");
147
148 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
149 "\t\\_[WARNING] - foobar\n"
150 "\t\t\\_[OK] - baz\n"
151 "|foo=23s;;; \n";
152
153 // diag("Expected output. Length: %u", strlen(expected));
154 // diag(expected);
155
156 ok(strcmp(output, expected) == 0, "Output is as expected");
157}
158
159void test_deep_check_hierarchy(void) {
160 // level 4
161 mp_subcheck sc4 = mp_subcheck_init();
162 sc4.output = "level4";
163 sc4 = mp_set_subcheck_state(sc4, STATE_OK);
164
165 // level 3
166 mp_subcheck sc3 = mp_subcheck_init();
167 sc3.output = "level3";
168 sc3 = mp_set_subcheck_state(sc3, STATE_OK);
169
170 // level 2
171 mp_subcheck sc2 = mp_subcheck_init();
172 sc2.output = "baz";
173 sc2 = mp_set_subcheck_state(sc2, STATE_OK);
174
175 // level 1
176 mp_subcheck sc1 = mp_subcheck_init();
177
178 sc1.output = "foobar";
179 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
180
181 mp_perfdata pd1 = perfdata_init();
182
183 pd1.uom = "s";
184 pd1.label = "foo";
185 pd1 = mp_set_pd_value(pd1, 23);
186
187 mp_add_perfdata_to_subcheck(&sc1, pd1);
188
189 // main check
190 mp_check check = mp_check_init();
191
192 mp_add_subcheck_to_subcheck(&sc3, sc4);
193 mp_add_subcheck_to_subcheck(&sc2, sc3);
194 mp_add_subcheck_to_subcheck(&sc1, sc2);
195 mp_add_subcheck_to_check(&check, sc1);
196
197 char *output = mp_fmt_output(check);
198
199 size_t output_length = strlen(output);
200
201 // diag("Formatted output of length %i", output_length);
202 // diag(output);
203
204 ok(output != NULL, "Output should not be NULL");
205
206 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
207 "\t\\_[WARNING] - foobar\n"
208 "\t\t\\_[OK] - baz\n"
209 "\t\t\t\\_[OK] - level3\n"
210 "\t\t\t\t\\_[OK] - level4\n"
211 "|foo=23s;;; \n";
212
213 size_t expected_length = strlen(expected);
214
215 // diag("Expected output of length: %i", expected_length);
216 // diag(expected);
217
218 ok(output_length == expected_length, "Outputs are of equal length");
219 ok(strcmp(output, expected) == 0, "Output is as expected");
220}
221
222void test_deep_check_hierarchy2(void) {
223 // level 1
224 mp_subcheck sc1 = mp_subcheck_init();
225
226 sc1.output = "foobar";
227 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
228
229 mp_perfdata pd1 = perfdata_init();
230 pd1.uom = "s";
231 pd1.label = "foo";
232 pd1 = mp_set_pd_value(pd1, 23);
233
234 mp_add_perfdata_to_subcheck(&sc1, pd1);
235
236 // level 2
237 mp_subcheck sc2 = mp_subcheck_init();
238 sc2.output = "baz";
239 sc2 = mp_set_subcheck_state(sc2, STATE_OK);
240
241 mp_perfdata pd2 = perfdata_init();
242 pd2.uom = "B";
243 pd2.label = "baz";
244 pd2 = mp_set_pd_value(pd2, 1024);
245 mp_add_perfdata_to_subcheck(&sc2, pd2);
246
247 // level 3
248 mp_subcheck sc3 = mp_subcheck_init();
249 sc3.output = "level3";
250 sc3 = mp_set_subcheck_state(sc3, STATE_OK);
251
252 mp_perfdata pd3 = perfdata_init();
253 pd3.label = "floatMe";
254 pd3 = mp_set_pd_value(pd3, 1024.1024);
255 mp_add_perfdata_to_subcheck(&sc3, pd3);
256
257 // level 4
258 mp_subcheck sc4 = mp_subcheck_init();
259 sc4.output = "level4";
260 sc4 = mp_set_subcheck_state(sc4, STATE_OK);
261
262 mp_check check = mp_check_init();
263
264 mp_add_subcheck_to_subcheck(&sc3, sc4);
265 mp_add_subcheck_to_subcheck(&sc2, sc3);
266 mp_add_subcheck_to_subcheck(&sc1, sc2);
267 mp_add_subcheck_to_check(&check, sc1);
268
269 char *output = mp_fmt_output(check);
270
271 // diag("Formatted output of length: %i", strlen(output));
272 // diag(output);
273
274 ok(output != NULL, "Output should not be NULL");
275
276 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
277 "\t\\_[WARNING] - foobar\n"
278 "\t\t\\_[OK] - baz\n"
279 "\t\t\t\\_[OK] - level3\n"
280 "\t\t\t\t\\_[OK] - level4\n"
281 "|foo=23s;;; baz=1024B;;; floatMe=1024.102400;;; \n";
282
283 // diag("Expected output of length: %i", strlen(expected));
284 // diag(expected);
285
286 ok(strcmp(output, expected) == 0, "Output is as expected");
287}
288
289void test_default_states1(void) {
290 mp_subcheck sc = mp_subcheck_init();
291
292 mp_state_enum state1 = mp_compute_subcheck_state(sc);
293 ok(state1 == STATE_UNKNOWN, "Default default state is Unknown");
294
295 sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
296
297 mp_state_enum state2 = mp_compute_subcheck_state(sc);
298 ok(state2 == STATE_CRITICAL, "Default state is Critical");
299
300 sc = mp_set_subcheck_state(sc, STATE_OK);
301
302 mp_state_enum state3 = mp_compute_subcheck_state(sc);
303 ok(state3 == STATE_OK, "Default state is Critical");
304}
305
306void test_default_states2(void) {
307 mp_check check = mp_check_init();
308
309 mp_subcheck sc = mp_subcheck_init();
310 sc.output = "placeholder";
311 sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
312
313 mp_add_subcheck_to_check(&check, sc);
314
315 mp_state_enum result_state = mp_compute_check_state(check);
316 ok(result_state == STATE_CRITICAL, "Derived state is the proper default state");
317}
diff --git a/lib/tests/test_generic_output.t b/lib/tests/test_generic_output.t
new file mode 100644
index 00000000..48c2ddf4
--- /dev/null
+++ b/lib/tests/test_generic_output.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_generic_output") {
4 plan skip_all => "./test_generic_output not compiled - please enable libtap library to test";
5}
6exec "./test_generic_output";
diff --git a/lib/tests/test_ini1.c b/lib/tests/test_ini1.c
index 246c1250..de983764 100644
--- a/lib/tests/test_ini1.c
+++ b/lib/tests/test_ini1.c
@@ -42,27 +42,30 @@ char *list2str(np_arg_list *optlst) {
42 free(optltmp); 42 free(optltmp);
43 } 43 }
44 /* Strip last whitespace */ 44 /* Strip last whitespace */
45 if (strlen(optstr) > 1) 45 if (strlen(optstr) > 1) {
46 optstr[strlen(optstr) - 1] = '\0'; 46 optstr[strlen(optstr) - 1] = '\0';
47 }
47 48
48 return optstr; 49 return optstr;
49} 50}
50 51
51int main(int argc, char **argv) { 52int main(int argc, char **argv) {
52 char *optstr = NULL;
53 53
54 plan_tests(12); 54 plan_tests(12);
55 55
56 optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk")); 56 char *optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk"));
57 ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "config-tiny.ini's section as expected"); 57 ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"),
58 "config-tiny.ini's section as expected");
58 my_free(optstr); 59 my_free(optstr);
59 60
60 optstr = list2str(np_get_defaults("@./config-tiny.ini", "section")); 61 optstr = list2str(np_get_defaults("@./config-tiny.ini", "section"));
61 ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "Used default section name, without specific"); 62 ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"),
63 "Used default section name, without specific");
62 my_free(optstr); 64 my_free(optstr);
63 65
64 optstr = list2str(np_get_defaults("Section Two@./config-tiny.ini", "check_disk")); 66 optstr = list2str(np_get_defaults("Section Two@./config-tiny.ini", "check_disk"));
65 ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), "config-tiny.ini's Section Two as expected"); 67 ok(!strcmp(optstr, "--something else=blah --remove=whitespace"),
68 "config-tiny.ini's Section Two as expected");
66 my_free(optstr); 69 my_free(optstr);
67 70
68 optstr = list2str(np_get_defaults("/path/to/file.txt@./config-tiny.ini", "check_disk")); 71 optstr = list2str(np_get_defaults("/path/to/file.txt@./config-tiny.ini", "check_disk"));
@@ -70,15 +73,18 @@ int main(int argc, char **argv) {
70 my_free(optstr); 73 my_free(optstr);
71 74
72 optstr = list2str(np_get_defaults("section2@./config-tiny.ini", "check_disk")); 75 optstr = list2str(np_get_defaults("section2@./config-tiny.ini", "check_disk"));
73 ok(!strcmp(optstr, "--this=that"), "config-tiny.ini's section2 with whitespace before section name"); 76 ok(!strcmp(optstr, "--this=that"),
77 "config-tiny.ini's section2 with whitespace before section name");
74 my_free(optstr); 78 my_free(optstr);
75 79
76 optstr = list2str(np_get_defaults("section3@./config-tiny.ini", "check_disk")); 80 optstr = list2str(np_get_defaults("section3@./config-tiny.ini", "check_disk"));
77 ok(!strcmp(optstr, "--this=that"), "config-tiny.ini's section3 with whitespace after section name"); 81 ok(!strcmp(optstr, "--this=that"),
82 "config-tiny.ini's section3 with whitespace after section name");
78 my_free(optstr); 83 my_free(optstr);
79 84
80 optstr = list2str(np_get_defaults("check_mysql@./plugin.ini", "check_disk")); 85 optstr = list2str(np_get_defaults("check_mysql@./plugin.ini", "check_disk"));
81 ok(!strcmp(optstr, "--username=operator --password=secret"), "plugin.ini's check_mysql as expected"); 86 ok(!strcmp(optstr, "--username=operator --password=secret"),
87 "plugin.ini's check_mysql as expected");
82 my_free(optstr); 88 my_free(optstr);
83 89
84 optstr = list2str(np_get_defaults("check_mysql2@./plugin.ini", "check_disk")); 90 optstr = list2str(np_get_defaults("check_mysql2@./plugin.ini", "check_disk"));
@@ -90,29 +96,39 @@ int main(int argc, char **argv) {
90 my_free(optstr); 96 my_free(optstr);
91 97
92 optstr = list2str(np_get_defaults("Section Two@./config-dos.ini", "check_disk")); 98 optstr = list2str(np_get_defaults("Section Two@./config-dos.ini", "check_disk"));
93 ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), "config-dos.ini's Section Two as expected"); 99 ok(!strcmp(optstr, "--something else=blah --remove=whitespace"),
100 "config-dos.ini's Section Two as expected");
94 my_free(optstr); 101 my_free(optstr);
95 102
96 optstr = list2str(np_get_defaults("section_twice@./plugin.ini", "check_disk")); 103 optstr = list2str(np_get_defaults("section_twice@./plugin.ini", "check_disk"));
97 ok(!strcmp(optstr, "--foo=bar --bar=foo"), "plugin.ini's section_twice defined twice in the file"); 104 ok(!strcmp(optstr, "--foo=bar --bar=foo"),
105 "plugin.ini's section_twice defined twice in the file");
98 my_free(optstr); 106 my_free(optstr);
99 107
100 optstr = list2str(np_get_defaults("tcp_long_lines@plugins.ini", "check_tcp")); 108 optstr = list2str(np_get_defaults("tcp_long_lines@plugins.ini", "check_tcp"));
101 ok(!strcmp(optstr, "--escape --send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " 109 ok(!strcmp(optstr, "--escape --send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
110 "yadda Foo bar BAZ yadda yadda yadda Foo bar "
102 "BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 111 "BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
103 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " 112 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar "
113 "BAZ yadda yadda yadda Foo bar BAZ yadda "
104 "yadda yadda Foo bar BAZ yadda yadda yadda Foo " 114 "yadda yadda Foo bar BAZ yadda yadda yadda Foo "
105 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 115 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda "
116 "yadda yadda Foo bar BAZ yadda yadda "
106 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " 117 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
107 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda --expect=Foo bar BAZ yadda yadda " 118 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
119 "yadda --expect=Foo bar BAZ yadda yadda "
108 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " 120 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
109 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo " 121 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
122 "yadda Foo bar BAZ yadda yadda yadda Foo "
110 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 123 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
111 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " 124 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar "
125 "BAZ yadda yadda yadda Foo bar BAZ yadda "
112 "yadda yadda Foo bar BAZ yadda yadda yadda Foo " 126 "yadda yadda Foo bar BAZ yadda yadda yadda Foo "
113 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 127 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda "
128 "yadda yadda Foo bar BAZ yadda yadda "
114 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " 129 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
115 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo " 130 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
131 "yadda Foo bar BAZ yadda yadda yadda Foo "
116 "bar BAZ yadda yadda yadda --jail"), 132 "bar BAZ yadda yadda yadda --jail"),
117 "Long options"); 133 "Long options");
118 my_free(optstr); 134 my_free(optstr);
diff --git a/lib/tests/test_opts1.c b/lib/tests/test_opts1.c
index 984183d3..fa95c4d4 100644
--- a/lib/tests/test_opts1.c
+++ b/lib/tests/test_opts1.c
@@ -40,37 +40,40 @@ void my_free(int *argc, char **newargv, char **argv) {
40#else 40#else
41void my_free(int *argc, char **newargv, char **argv) { 41void my_free(int *argc, char **newargv, char **argv) {
42 /* Free stuff (and print while we're at it) */ 42 /* Free stuff (and print while we're at it) */
43 int i, freeflag = 1; 43 bool freeflag = true;
44 printf(" Arg(%i): ", *argc + 1); 44 printf(" Arg(%i): ", *argc + 1);
45 printf("'%s' ", newargv[0]); 45 printf("'%s' ", newargv[0]);
46 for (i = 1; i < *argc; i++) { 46
47 for (int i = 1; i < *argc; i++) {
47 printf("'%s' ", newargv[i]); 48 printf("'%s' ", newargv[i]);
48 /* Stop freeing when we get to the start of the original array */ 49 /* Stop freeing when we get to the start of the original array */
49 if (freeflag) { 50 if (freeflag) {
50 if (newargv[i] == argv[1]) 51 if (newargv[i] == argv[1]) {
51 freeflag = 0; 52 freeflag = false;
52 else 53 } else {
53 free(newargv[i]); 54 free(newargv[i]);
55 }
54 } 56 }
55 } 57 }
56 printf("\n"); 58 printf("\n");
57 /* Free only if it's a different array */ 59 /* Free only if it's a different array */
58 if (newargv != argv) 60 if (newargv != argv) {
59 free(newargv); 61 free(newargv);
62 }
60 *argc = 0; 63 *argc = 0;
61} 64}
62#endif 65#endif
63 66
64int array_diff(int i1, char **a1, int i2, char **a2) { 67int array_diff(int i1, char **a1, int i2, char **a2) {
65 int i;
66
67 if (i1 != i2) { 68 if (i1 != i2) {
68 printf(" Argument count doesn't match!\n"); 69 printf(" Argument count doesn't match!\n");
69 return 0; 70 return 0;
70 } 71 }
71 for (i = 0; i <= i1; i++) { 72
72 if (a1[i] == NULL && a2[i] == NULL) 73 for (int i = 0; i <= i1; i++) {
74 if (a1[i] == NULL && a2[i] == NULL) {
73 continue; 75 continue;
76 }
74 if (a1[i] == NULL || a2[i] == NULL) { 77 if (a1[i] == NULL || a2[i] == NULL) {
75 printf(" Argument # %i null in one array!\n", i); 78 printf(" Argument # %i null in one array!\n", i);
76 return 0; 79 return 0;
@@ -84,11 +87,10 @@ int array_diff(int i1, char **a1, int i2, char **a2) {
84} 87}
85 88
86int main(int argc, char **argv) { 89int main(int argc, char **argv) {
87 char **argv_new = NULL;
88 int i, argc_test;
89
90 plan_tests(5); 90 plan_tests(5);
91 91
92 char **argv_new = NULL;
93 int argc_test;
92 { 94 {
93 char *argv_test[] = {"prog_name", (char *)NULL}; 95 char *argv_test[] = {"prog_name", (char *)NULL};
94 argc_test = 1; 96 argc_test = 1;
@@ -110,27 +112,36 @@ int main(int argc, char **argv) {
110 { 112 {
111 char *argv_test[] = {"prog_name", "--extra-opts=@./config-opts.ini", (char *)NULL}; 113 char *argv_test[] = {"prog_name", "--extra-opts=@./config-opts.ini", (char *)NULL};
112 argc_test = 2; 114 argc_test = 2;
113 char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", (char *)NULL}; 115 char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank",
116 (char *)NULL};
114 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); 117 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk");
115 ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts using default section"); 118 ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts using default section");
116 my_free(&argc_test, argv_new, argv_test); 119 my_free(&argc_test, argv_new, argv_test);
117 } 120 }
118 121
119 { 122 {
120 char *argv_test[] = {"prog_name", "--extra-opts=sect1@./config-opts.ini", "--extra-opts", "sect2@./config-opts.ini", (char *)NULL}; 123 char *argv_test[] = {"prog_name", "--extra-opts=sect1@./config-opts.ini", "--extra-opts",
124 "sect2@./config-opts.ini", (char *)NULL};
121 argc_test = 4; 125 argc_test = 4;
122 char *argv_known[] = {"prog_name", "--one=two", "--something else=oops", "--this=that", (char *)NULL}; 126 char *argv_known[] = {"prog_name", "--one=two", "--something else=oops", "--this=that",
127 (char *)NULL};
123 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); 128 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk");
124 ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts specified twice"); 129 ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts specified twice");
125 my_free(&argc_test, argv_new, argv_test); 130 my_free(&argc_test, argv_new, argv_test);
126 } 131 }
127 132
128 { 133 {
129 char *argv_test[] = {"prog_name", "--arg1=val1", "--extra-opts=@./config-opts.ini", "--extra-opts", "sect1@./config-opts.ini", 134 char *argv_test[] = {"prog_name",
130 "--arg2", (char *)NULL}; 135 "--arg1=val1",
136 "--extra-opts=@./config-opts.ini",
137 "--extra-opts",
138 "sect1@./config-opts.ini",
139 "--arg2",
140 (char *)NULL};
131 argc_test = 6; 141 argc_test = 6;
132 char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", "--one=two", 142 char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!",
133 "--arg1=val1", "--arg2", (char *)NULL}; 143 "--blank", "--one=two", "--arg1=val1",
144 "--arg2", (char *)NULL};
134 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); 145 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk");
135 ok(array_diff(argc_test, argv_new, 7, argv_known), "twice extra opts using two sections"); 146 ok(array_diff(argc_test, argv_new, 7, argv_known), "twice extra opts using two sections");
136 my_free(&argc_test, argv_new, argv_test); 147 my_free(&argc_test, argv_new, argv_test);
diff --git a/lib/tests/test_opts2.c b/lib/tests/test_opts2.c
index 23496617..3dd1b039 100644
--- a/lib/tests/test_opts2.c
+++ b/lib/tests/test_opts2.c
@@ -23,36 +23,39 @@
23 23
24void my_free(int *argc, char **newargv, char **argv) { 24void my_free(int *argc, char **newargv, char **argv) {
25 /* Free stuff (and print while we're at it) */ 25 /* Free stuff (and print while we're at it) */
26 int i, freeflag = 1; 26 bool freeflag = true;
27
27 printf(" Arg(%i): ", *argc + 1); 28 printf(" Arg(%i): ", *argc + 1);
28 printf("'%s' ", newargv[0]); 29 printf("'%s' ", newargv[0]);
29 for (i = 1; i < *argc; i++) { 30 for (int i = 1; i < *argc; i++) {
30 printf("'%s' ", newargv[i]); 31 printf("'%s' ", newargv[i]);
31 /* Stop freeing when we get to the start of the original array */ 32 /* Stop freeing when we get to the start of the original array */
32 if (freeflag) { 33 if (freeflag) {
33 if (newargv[i] == argv[1]) 34 if (newargv[i] == argv[1]) {
34 freeflag = 0; 35 freeflag = false;
35 else 36 } else {
36 free(newargv[i]); 37 free(newargv[i]);
38 }
37 } 39 }
38 } 40 }
39 printf("\n"); 41 printf("\n");
40 /* Free only if it's a different array */ 42 /* Free only if it's a different array */
41 if (newargv != argv) 43 if (newargv != argv) {
42 free(newargv); 44 free(newargv);
45 }
43 *argc = 0; 46 *argc = 0;
44} 47}
45 48
46int array_diff(int i1, char **a1, int i2, char **a2) { 49int array_diff(int i1, char **a1, int i2, char **a2) {
47 int i;
48
49 if (i1 != i2) { 50 if (i1 != i2) {
50 printf(" Argument count doesn't match!\n"); 51 printf(" Argument count doesn't match!\n");
51 return 0; 52 return 0;
52 } 53 }
53 for (i = 0; i <= i1; i++) { 54
54 if (a1[i] == NULL && a2[i] == NULL) 55 for (int i = 0; i <= i1; i++) {
56 if (a1[i] == NULL && a2[i] == NULL) {
55 continue; 57 continue;
58 }
56 if (a1[i] == NULL || a2[i] == NULL) { 59 if (a1[i] == NULL || a2[i] == NULL) {
57 printf(" Argument # %i null in one array!\n", i); 60 printf(" Argument # %i null in one array!\n", i);
58 return 0; 61 return 0;
@@ -66,11 +69,10 @@ int array_diff(int i1, char **a1, int i2, char **a2) {
66} 69}
67 70
68int main(int argc, char **argv) { 71int main(int argc, char **argv) {
69 char **argv_new = NULL;
70 int i, argc_test;
71
72 plan_tests(5); 72 plan_tests(5);
73 73
74 char **argv_new = NULL;
75 int argc_test;
74 { 76 {
75 char *argv_test[] = {"prog_name", "arg1", "--extra-opts", "--arg3", "val2", (char *)NULL}; 77 char *argv_test[] = {"prog_name", "arg1", "--extra-opts", "--arg3", "val2", (char *)NULL};
76 argc_test = 5; 78 argc_test = 5;
@@ -90,7 +92,8 @@ int main(int argc, char **argv) {
90 } 92 }
91 93
92 { 94 {
93 char *argv_test[] = {"prog_name", "arg1", "--extra-opts=section1", "--arg3", "val2", (char *)NULL}; 95 char *argv_test[] = {"prog_name", "arg1", "--extra-opts=section1",
96 "--arg3", "val2", (char *)NULL};
94 argc_test = 5; 97 argc_test = 5;
95 char *argv_known[] = {"prog_name", "--foobar=baz", "arg1", "--arg3", "val2", (char *)NULL}; 98 char *argv_known[] = {"prog_name", "--foobar=baz", "arg1", "--arg3", "val2", (char *)NULL};
96 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); 99 argv_new = np_extra_opts(&argc_test, argv_test, "check_disk");
@@ -108,30 +111,39 @@ int main(int argc, char **argv) {
108 } 111 }
109 112
110 { 113 {
111 char *argv_test[] = {"check_tcp", "--extra-opts", "--extra-opts=tcp_long_lines", (char *)NULL}; 114 char *argv_test[] = {"check_tcp", "--extra-opts", "--extra-opts=tcp_long_lines",
115 (char *)NULL};
112 argc_test = 3; 116 argc_test = 3;
113 char *argv_known[] = { 117 char *argv_known[] = {"check_tcp",
114 "check_tcp", 118 "--timeout=10",
115 "--timeout=10", 119 "--escape",
116 "--escape", 120 "--send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda "
117 "--send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 121 "Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
118 "yadda Foo bar BAZ yadda " 122 "yadda Foo bar BAZ yadda "
119 "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 123 "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
120 "yadda Foo bar BAZ " 124 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
121 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " 125 "yadda Foo bar BAZ "
122 "yadda yadda Foo bar " 126 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda "
123 "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda", 127 "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda "
124 "--expect=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 128 "yadda yadda Foo bar "
125 "yadda Foo bar BAZ yadda " 129 "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
126 "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " 130 "yadda yadda yadda Foo bar BAZ yadda yadda yadda",
127 "yadda Foo bar BAZ " 131 "--expect=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
128 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " 132 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
129 "yadda yadda Foo bar " 133 "yadda Foo bar BAZ yadda "
130 "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " 134 "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
131 "yadda yadda yadda Foo " 135 "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda "
132 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda", 136 "yadda Foo bar BAZ "
133 "--jail", 137 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda "
134 (char *)NULL}; 138 "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda "
139 "yadda yadda Foo bar "
140 "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
141 "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
142 "yadda yadda yadda Foo "
143 "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ "
144 "yadda yadda yadda Foo bar BAZ yadda yadda yadda",
145 "--jail",
146 (char *)NULL};
135 argv_new = np_extra_opts(&argc_test, argv_test, "check_tcp"); 147 argv_new = np_extra_opts(&argc_test, argv_test, "check_tcp");
136 ok(array_diff(argc_test, argv_new, 6, argv_known), "Long lines test"); 148 ok(array_diff(argc_test, argv_new, 6, argv_known), "Long lines test");
137 my_free(&argc_test, argv_new, argv_test); 149 my_free(&argc_test, argv_new, argv_test);
diff --git a/lib/tests/test_tcp.c b/lib/tests/test_tcp.c
index 1b3003e9..37c818c9 100644
--- a/lib/tests/test_tcp.c
+++ b/lib/tests/test_tcp.c
@@ -21,30 +21,38 @@
21#include "tap.h" 21#include "tap.h"
22 22
23int main(void) { 23int main(void) {
24 char **server_expect;
25 int server_expect_count = 3;
26
27 plan_tests(9); 24 plan_tests(9);
28 25
26 char **server_expect;
27 const int server_expect_count = 3;
29 server_expect = malloc(sizeof(char *) * server_expect_count); 28 server_expect = malloc(sizeof(char *) * server_expect_count);
30 29
31 server_expect[0] = strdup("AA"); 30 server_expect[0] = strdup("AA");
32 server_expect[1] = strdup("bb"); 31 server_expect[1] = strdup("bb");
33 server_expect[2] = strdup("CC"); 32 server_expect[2] = strdup("CC");
34 33
35 ok(np_expect_match("AA bb CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_SUCCESS, 34 ok(np_expect_match("AA bb CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) ==
35 NP_MATCH_SUCCESS,
36 "Test matching any string at the beginning (first expect string)"); 36 "Test matching any string at the beginning (first expect string)");
37 ok(np_expect_match("bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_SUCCESS, 37 ok(np_expect_match("bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) ==
38 NP_MATCH_SUCCESS,
38 "Test matching any string at the beginning (second expect string)"); 39 "Test matching any string at the beginning (second expect string)");
39 ok(np_expect_match("b", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_RETRY, 40 ok(np_expect_match("b", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_RETRY,
40 "Test matching any string at the beginning (substring match)"); 41 "Test matching any string at the beginning (substring match)");
41 ok(np_expect_match("XX bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_FAILURE, 42 ok(np_expect_match("XX bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) ==
43 NP_MATCH_FAILURE,
42 "Test with strings not matching at the beginning"); 44 "Test with strings not matching at the beginning");
43 ok(np_expect_match("XX CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_FAILURE, "Test matching any string"); 45 ok(np_expect_match("XX CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) ==
44 ok(np_expect_match("XX", server_expect, server_expect_count, 0) == NP_MATCH_RETRY, "Test not matching any string"); 46 NP_MATCH_FAILURE,
45 ok(np_expect_match("XX AA bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_SUCCESS, 47 "Test matching any string");
48 ok(np_expect_match("XX", server_expect, server_expect_count, 0) == NP_MATCH_RETRY,
49 "Test not matching any string");
50 ok(np_expect_match("XX AA bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) ==
51 NP_MATCH_SUCCESS,
46 "Test matching all strings"); 52 "Test matching all strings");
47 ok(np_expect_match("XX bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, "Test not matching all strings"); 53 ok(np_expect_match("XX bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) ==
54 NP_MATCH_RETRY,
55 "Test not matching all strings");
48 ok(np_expect_match("XX XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, 56 ok(np_expect_match("XX XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY,
49 "Test not matching any string (testing all)"); 57 "Test not matching any string (testing all)");
50 58
diff --git a/lib/tests/test_utils.c b/lib/tests/test_utils.c
index c3150f00..8040dec8 100644
--- a/lib/tests/test_utils.c
+++ b/lib/tests/test_utils.c
@@ -28,17 +28,7 @@
28#include "utils_base.c" 28#include "utils_base.c"
29 29
30int main(int argc, char **argv) { 30int main(int argc, char **argv) {
31 char state_path[1024]; 31 plan_tests(155);
32 range *range;
33 double temp;
34 thresholds *thresholds = NULL;
35 int i, rc;
36 char *temp_string;
37 state_key *temp_state_key = NULL;
38 state_data *temp_state_data;
39 time_t current_time;
40
41 plan_tests(185);
42 32
43 ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised"); 33 ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised");
44 34
@@ -57,7 +47,7 @@ int main(int argc, char **argv) {
57 47
58 np_set_args(argc, argv); 48 np_set_args(argc, argv);
59 49
60 range = parse_range_string("6"); 50 range *range = parse_range_string("6");
61 ok(range != NULL, "'6' is valid range"); 51 ok(range != NULL, "'6' is valid range");
62 ok(range->start == 0, "Start correct"); 52 ok(range->start == 0, "Start correct");
63 ok(range->start_infinity == false, "Not using negative infinity"); 53 ok(range->start_infinity == false, "Not using negative infinity");
@@ -97,7 +87,7 @@ int main(int argc, char **argv) {
97 free(range); 87 free(range);
98 88
99 range = parse_range_string("12345678901234567890:"); 89 range = parse_range_string("12345678901234567890:");
100 temp = atof("12345678901234567890"); /* Can't just use this because number too large */ 90 double temp = atof("12345678901234567890"); /* Can't just use this because number too large */
101 ok(range != NULL, "'12345678901234567890:' is valid range"); 91 ok(range != NULL, "'12345678901234567890:' is valid range");
102 ok(range->start == temp, "Start correct"); 92 ok(range->start == temp, "Start correct");
103 ok(range->start_infinity == false, "Not using negative infinity"); 93 ok(range->start_infinity == false, "Not using negative infinity");
@@ -158,32 +148,34 @@ int main(int argc, char **argv) {
158 range = parse_range_string("2:1"); 148 range = parse_range_string("2:1");
159 ok(range == NULL, "'2:1' rejected"); 149 ok(range == NULL, "'2:1' rejected");
160 150
161 rc = _set_thresholds(&thresholds, NULL, NULL); 151 thresholds *thresholds = NULL;
162 ok(rc == 0, "Thresholds (NULL, NULL) set"); 152 int returnCode;
153 returnCode = _set_thresholds(&thresholds, NULL, NULL);
154 ok(returnCode == 0, "Thresholds (NULL, NULL) set");
163 ok(thresholds->warning == NULL, "Warning not set"); 155 ok(thresholds->warning == NULL, "Warning not set");
164 ok(thresholds->critical == NULL, "Critical not set"); 156 ok(thresholds->critical == NULL, "Critical not set");
165 157
166 rc = _set_thresholds(&thresholds, NULL, "80"); 158 returnCode = _set_thresholds(&thresholds, NULL, "80");
167 ok(rc == 0, "Thresholds (NULL, '80') set"); 159 ok(returnCode == 0, "Thresholds (NULL, '80') set");
168 ok(thresholds->warning == NULL, "Warning not set"); 160 ok(thresholds->warning == NULL, "Warning not set");
169 ok(thresholds->critical->end == 80, "Critical set correctly"); 161 ok(thresholds->critical->end == 80, "Critical set correctly");
170 162
171 rc = _set_thresholds(&thresholds, "5:33", NULL); 163 returnCode = _set_thresholds(&thresholds, "5:33", NULL);
172 ok(rc == 0, "Thresholds ('5:33', NULL) set"); 164 ok(returnCode == 0, "Thresholds ('5:33', NULL) set");
173 ok(thresholds->warning->start == 5, "Warning start set"); 165 ok(thresholds->warning->start == 5, "Warning start set");
174 ok(thresholds->warning->end == 33, "Warning end set"); 166 ok(thresholds->warning->end == 33, "Warning end set");
175 ok(thresholds->critical == NULL, "Critical not set"); 167 ok(thresholds->critical == NULL, "Critical not set");
176 168
177 rc = _set_thresholds(&thresholds, "30", "60"); 169 returnCode = _set_thresholds(&thresholds, "30", "60");
178 ok(rc == 0, "Thresholds ('30', '60') set"); 170 ok(returnCode == 0, "Thresholds ('30', '60') set");
179 ok(thresholds->warning->end == 30, "Warning set correctly"); 171 ok(thresholds->warning->end == 30, "Warning set correctly");
180 ok(thresholds->critical->end == 60, "Critical set correctly"); 172 ok(thresholds->critical->end == 60, "Critical set correctly");
181 ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok"); 173 ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok");
182 ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning"); 174 ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning");
183 ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical"); 175 ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical");
184 176
185 rc = _set_thresholds(&thresholds, "-10:-2", "-30:20"); 177 returnCode = _set_thresholds(&thresholds, "-10:-2", "-30:20");
186 ok(rc == 0, "Thresholds ('-30:20', '-10:-2') set"); 178 ok(returnCode == 0, "Thresholds ('-30:20', '-10:-2') set");
187 ok(thresholds->warning->start == -10, "Warning start set correctly"); 179 ok(thresholds->warning->start == -10, "Warning start set correctly");
188 ok(thresholds->warning->end == -2, "Warning end set correctly"); 180 ok(thresholds->warning->end == -2, "Warning end set correctly");
189 ok(thresholds->critical->start == -30, "Critical start set correctly"); 181 ok(thresholds->critical->start == -30, "Critical start set correctly");
@@ -304,164 +296,28 @@ int main(int argc, char **argv) {
304 test = np_extract_ntpvar("", "foo"); 296 test = np_extract_ntpvar("", "foo");
305 ok(!test, "Empty string return NULL"); 297 ok(!test, "Empty string return NULL");
306 298
307 /* This is the result of running ./test_utils */
308 temp_string = (char *)_np_state_generate_key();
309 ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got hash with exe and no parameters") ||
310 diag("You are probably running in wrong directory. Must run as ./test_utils");
311
312 this_monitoring_plugin->argc = 4;
313 this_monitoring_plugin->argv[0] = "./test_utils";
314 this_monitoring_plugin->argv[1] = "here";
315 this_monitoring_plugin->argv[2] = "--and";
316 this_monitoring_plugin->argv[3] = "now";
317 temp_string = (char *)_np_state_generate_key();
318 ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), "Got based on expected argv");
319
320 unsetenv("MP_STATE_PATH");
321 temp_string = (char *)_np_state_calculate_location_prefix();
322 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory");
323
324 setenv("MP_STATE_PATH", "", 1);
325 temp_string = (char *)_np_state_calculate_location_prefix();
326 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string");
327
328 setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1);
329 temp_string = (char *)_np_state_calculate_location_prefix();
330 ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory");
331
332 ok(temp_state_key == NULL, "temp_state_key initially empty");
333
334 this_monitoring_plugin->argc = 1;
335 this_monitoring_plugin->argv[0] = "./test_utils";
336 np_enable_state(NULL, 51);
337 temp_state_key = this_monitoring_plugin->state;
338 ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name");
339 ok(!strcmp(temp_state_key->name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got generated filename");
340
341 np_enable_state("allowedchars_in_keyname", 77);
342 temp_state_key = this_monitoring_plugin->state;
343 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", (unsigned long)geteuid());
344 ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name");
345 ok(!strcmp(temp_state_key->name, "allowedchars_in_keyname"), "Got key name with valid chars");
346 ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename");
347
348 /* Don't do this test just yet. Will die */
349 /*
350 np_enable_state("bad^chars$in@here", 77);
351 temp_state_key = this_monitoring_plugin->state;
352 ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" );
353 */
354
355 np_enable_state("funnykeyname", 54);
356 temp_state_key = this_monitoring_plugin->state;
357 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", (unsigned long)geteuid());
358 ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name");
359 ok(!strcmp(temp_state_key->name, "funnykeyname"), "Got key name");
360
361 ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename");
362 ok(temp_state_key->data_version == 54, "Version set");
363
364 temp_state_data = np_state_read();
365 ok(temp_state_data == NULL, "Got no state data as file does not exist");
366
367 /*
368 temp_fp = fopen("var/statefile", "r");
369 if (temp_fp==NULL)
370 printf("Error opening. errno=%d\n", errno);
371 printf("temp_fp=%s\n", temp_fp);
372 ok( _np_state_read_file(temp_fp) == true, "Can read state file" );
373 fclose(temp_fp);
374 */
375
376 temp_state_key->_filename = "var/statefile";
377 temp_state_data = np_state_read();
378 ok(this_monitoring_plugin->state->state_data != NULL, "Got state data now") ||
379 diag("Are you running in right directory? Will get coredump next if not");
380 ok(this_monitoring_plugin->state->state_data->time == 1234567890, "Got time");
381 ok(!strcmp((char *)this_monitoring_plugin->state->state_data->data, "String to read"), "Data as expected");
382
383 temp_state_key->data_version = 53;
384 temp_state_data = np_state_read();
385 ok(temp_state_data == NULL, "Older data version gives NULL");
386 temp_state_key->data_version = 54;
387
388 temp_state_key->_filename = "var/nonexistent";
389 temp_state_data = np_state_read();
390 ok(temp_state_data == NULL, "Missing file gives NULL");
391 ok(this_monitoring_plugin->state->state_data == NULL, "No state information");
392
393 temp_state_key->_filename = "var/oldformat";
394 temp_state_data = np_state_read();
395 ok(temp_state_data == NULL, "Old file format gives NULL");
396
397 temp_state_key->_filename = "var/baddate";
398 temp_state_data = np_state_read();
399 ok(temp_state_data == NULL, "Bad date gives NULL");
400
401 temp_state_key->_filename = "var/missingdataline";
402 temp_state_data = np_state_read();
403 ok(temp_state_data == NULL, "Missing data line gives NULL");
404
405 unlink("var/generated");
406 temp_state_key->_filename = "var/generated";
407 current_time = 1234567890;
408 np_state_write_string(current_time, "String to read");
409 ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected");
410
411 unlink("var/generated_directory/statefile");
412 unlink("var/generated_directory");
413 temp_state_key->_filename = "var/generated_directory/statefile";
414 current_time = 1234567890;
415 np_state_write_string(current_time, "String to read");
416 ok(system("cmp var/generated_directory/statefile var/statefile") == 0, "Have created directory");
417
418 /* This test to check cannot write to dir - can't automate yet */
419 /*
420 unlink("var/generated_bad_dir");
421 mkdir("var/generated_bad_dir", S_IRUSR);
422 np_state_write_string(current_time, "String to read");
423 */
424
425 temp_state_key->_filename = "var/generated";
426 time(&current_time);
427 np_state_write_string(0, "String to read");
428 temp_state_data = np_state_read();
429 /* Check time is set to current_time */
430 ok(system("cmp var/generated var/statefile > /dev/null") != 0, "Generated file should be different this time");
431 ok(this_monitoring_plugin->state->state_data->time - current_time <= 1, "Has time generated from current time");
432
433 /* Don't know how to automatically test this. Need to be able to redefine die and catch the error */
434 /*
435 temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write";
436 np_state_write_string(0, "Bad file");
437 */
438
439 np_cleanup();
440
441 ok(this_monitoring_plugin == NULL, "Free'd this_monitoring_plugin");
442
443 ok(mp_suid() == false, "Test aren't suid"); 299 ok(mp_suid() == false, "Test aren't suid");
444 300
445 /* base states with random case */ 301 /* base states with random case */
446 char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; 302 char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL};
447 303
448 for (i = 0; states[i] != NULL; i++) { 304 for (int i = 0; states[i] != NULL; i++) {
449 /* out of the random case states, create the lower and upper versions + numeric string one */ 305 /* out of the random case states, create the lower and upper versions + numeric string one
306 */
450 char *statelower = strdup(states[i]); 307 char *statelower = strdup(states[i]);
451 char *stateupper = strdup(states[i]); 308 char *stateupper = strdup(states[i]);
452 char statenum[2]; 309 char statenum[2];
453 char *temp_ptr; 310 for (char *temp_ptr = statelower; *temp_ptr; temp_ptr++) {
454 for (temp_ptr = statelower; *temp_ptr; temp_ptr++) { 311 *temp_ptr = (char)tolower(*temp_ptr);
455 *temp_ptr = tolower(*temp_ptr);
456 } 312 }
457 for (temp_ptr = stateupper; *temp_ptr; temp_ptr++) { 313 for (char *temp_ptr = stateupper; *temp_ptr; temp_ptr++) {
458 *temp_ptr = toupper(*temp_ptr); 314 *temp_ptr = (char)toupper(*temp_ptr);
459 } 315 }
460 snprintf(statenum, 2, "%i", i); 316 snprintf(statenum, 2, "%i", i);
461 317
462 /* Base test names, we'll append the state string */ 318 /* Base test names, we'll append the state string */
463 char testname[64] = "Translate state string: "; 319 char testname[64] = "Translate state string: ";
464 int tlen = strlen(testname); 320 size_t tlen = strlen(testname);
465 321
466 strcpy(testname + tlen, states[i]); 322 strcpy(testname + tlen, states[i]);
467 ok(i == mp_translate_state(states[i]), testname); 323 ok(i == mp_translate_state(states[i]), testname);
diff --git a/lib/thresholds.c b/lib/thresholds.c
new file mode 100644
index 00000000..de2b9315
--- /dev/null
+++ b/lib/thresholds.c
@@ -0,0 +1,71 @@
1#include "./thresholds.h"
2#include "./utils_base.h"
3#include "perfdata.h"
4
5#include <stddef.h>
6
7mp_thresholds mp_thresholds_init() {
8 mp_thresholds tmp = {
9 .critical = {},
10 .critical_is_set = false,
11 .warning = {},
12 .warning_is_set = false,
13 };
14 return tmp;
15}
16
17char *fmt_threshold_warning(const thresholds th) {
18 if (th.warning == NULL) {
19 return "";
20 }
21
22 return fmt_range(*th.warning);
23}
24
25char *fmt_threshold_critical(const thresholds th) {
26 if (th.critical == NULL) {
27 return "";
28 }
29 return fmt_range(*th.critical);
30}
31
32mp_perfdata mp_pd_set_thresholds(mp_perfdata perfdata, mp_thresholds threshold) {
33 if (threshold.critical_is_set) {
34 perfdata.crit = threshold.critical;
35 perfdata.crit_present = true;
36 }
37
38 if (threshold.warning_is_set) {
39 perfdata.warn = threshold.warning;
40 perfdata.warn_present = true;
41 }
42
43 return perfdata;
44}
45
46mp_state_enum mp_get_pd_status(mp_perfdata perfdata) {
47 if (perfdata.crit_present) {
48 if (mp_check_range(perfdata.value, perfdata.crit)) {
49 return STATE_CRITICAL;
50 }
51 }
52 if (perfdata.warn_present) {
53 if (mp_check_range(perfdata.value, perfdata.warn)) {
54 return STATE_WARNING;
55 }
56 }
57
58 return STATE_OK;
59}
60
61mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn) {
62 thlds.warning = warn;
63 thlds.warning_is_set = true;
64 return thlds;
65}
66
67mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit) {
68 thlds.critical = crit;
69 thlds.critical_is_set = true;
70 return thlds;
71}
diff --git a/lib/thresholds.h b/lib/thresholds.h
new file mode 100644
index 00000000..f8647681
--- /dev/null
+++ b/lib/thresholds.h
@@ -0,0 +1,31 @@
1#pragma once
2
3#include "./perfdata.h"
4#include "states.h"
5
6/*
7 * Old threshold type using the old range type
8 */
9typedef struct {
10 range *warning;
11 range *critical;
12} thresholds;
13
14typedef struct {
15 bool warning_is_set;
16 mp_range warning;
17 bool critical_is_set;
18 mp_range critical;
19} mp_thresholds;
20
21mp_thresholds mp_thresholds_init(void);
22
23mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */);
24
25mp_state_enum mp_get_pd_status(mp_perfdata /* pd */);
26
27mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn);
28mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit);
29
30char *fmt_threshold_warning(thresholds th);
31char *fmt_threshold_critical(thresholds th);
diff --git a/lib/utils_base.c b/lib/utils_base.c
index 90a4aaa5..28e6dc47 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -25,6 +25,7 @@
25 *****************************************************************************/ 25 *****************************************************************************/
26 26
27#include "../plugins/common.h" 27#include "../plugins/common.h"
28#include "states.h"
28#include <stdarg.h> 29#include <stdarg.h>
29#include "utils_base.h" 30#include "utils_base.h"
30#include <ctype.h> 31#include <ctype.h>
@@ -33,20 +34,20 @@
33#include <unistd.h> 34#include <unistd.h>
34#include <sys/types.h> 35#include <sys/types.h>
35 36
36#define np_free(ptr) \ 37#define np_free(ptr) \
37 { \ 38 { \
38 if (ptr) { \ 39 if (ptr) { \
39 free(ptr); \ 40 free(ptr); \
40 ptr = NULL; \ 41 ptr = NULL; \
41 } \ 42 } \
42 } 43 }
43 44
44monitoring_plugin *this_monitoring_plugin = NULL; 45monitoring_plugin *this_monitoring_plugin = NULL;
45 46
46int timeout_state = STATE_CRITICAL; 47mp_state_enum timeout_state = STATE_CRITICAL;
47unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; 48unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
48 49
49bool _np_state_read_file(FILE *); 50bool _np_state_read_file(FILE *state_file);
50 51
51void np_init(char *plugin_name, int argc, char **argv) { 52void np_init(char *plugin_name, int argc, char **argv) {
52 if (this_monitoring_plugin == NULL) { 53 if (this_monitoring_plugin == NULL) {
@@ -55,31 +56,25 @@ void np_init(char *plugin_name, int argc, char **argv) {
55 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 56 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
56 } 57 }
57 this_monitoring_plugin->plugin_name = strdup(plugin_name); 58 this_monitoring_plugin->plugin_name = strdup(plugin_name);
58 if (this_monitoring_plugin->plugin_name == NULL) 59 if (this_monitoring_plugin->plugin_name == NULL) {
59 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); 60 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
61 }
60 this_monitoring_plugin->argc = argc; 62 this_monitoring_plugin->argc = argc;
61 this_monitoring_plugin->argv = argv; 63 this_monitoring_plugin->argv = argv;
62 } 64 }
63} 65}
64 66
65void np_set_args(int argc, char **argv) { 67void np_set_args(int argc, char **argv) {
66 if (this_monitoring_plugin == NULL) 68 if (this_monitoring_plugin == NULL) {
67 die(STATE_UNKNOWN, _("This requires np_init to be called")); 69 die(STATE_UNKNOWN, _("This requires np_init to be called"));
70 }
68 71
69 this_monitoring_plugin->argc = argc; 72 this_monitoring_plugin->argc = argc;
70 this_monitoring_plugin->argv = argv; 73 this_monitoring_plugin->argv = argv;
71} 74}
72 75
73void np_cleanup() { 76void np_cleanup(void) {
74 if (this_monitoring_plugin != NULL) { 77 if (this_monitoring_plugin != NULL) {
75 if (this_monitoring_plugin->state != NULL) {
76 if (this_monitoring_plugin->state->state_data) {
77 np_free(this_monitoring_plugin->state->state_data->data);
78 np_free(this_monitoring_plugin->state->state_data);
79 }
80 np_free(this_monitoring_plugin->state->name);
81 np_free(this_monitoring_plugin->state);
82 }
83 np_free(this_monitoring_plugin->plugin_name); 78 np_free(this_monitoring_plugin->plugin_name);
84 np_free(this_monitoring_plugin); 79 np_free(this_monitoring_plugin);
85 } 80 }
@@ -151,7 +146,8 @@ range *parse_range_string(char *str) {
151 set_range_end(temp_range, end); 146 set_range_end(temp_range, end);
152 } 147 }
153 148
154 if (temp_range->start_infinity == true || temp_range->end_infinity == true || temp_range->start <= temp_range->end) { 149 if (temp_range->start_infinity || temp_range->end_infinity ||
150 temp_range->start <= temp_range->end) {
155 return temp_range; 151 return temp_range;
156 } 152 }
157 free(temp_range); 153 free(temp_range);
@@ -162,8 +158,9 @@ range *parse_range_string(char *str) {
162int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) { 158int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) {
163 thresholds *temp_thresholds = NULL; 159 thresholds *temp_thresholds = NULL;
164 160
165 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) 161 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) {
166 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 162 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
163 }
167 164
168 temp_thresholds->warning = NULL; 165 temp_thresholds->warning = NULL;
169 temp_thresholds->critical = NULL; 166 temp_thresholds->critical = NULL;
@@ -202,12 +199,14 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
202 printf("Threshold not set"); 199 printf("Threshold not set");
203 } else { 200 } else {
204 if (my_threshold->warning) { 201 if (my_threshold->warning) {
205 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); 202 printf("Warning: start=%g end=%g; ", my_threshold->warning->start,
203 my_threshold->warning->end);
206 } else { 204 } else {
207 printf("Warning not set; "); 205 printf("Warning not set; ");
208 } 206 }
209 if (my_threshold->critical) { 207 if (my_threshold->critical) {
210 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); 208 printf("Critical: start=%g end=%g", my_threshold->critical->start,
209 my_threshold->critical->end);
211 } else { 210 } else {
212 printf("Critical not set"); 211 printf("Critical not set");
213 } 212 }
@@ -215,6 +214,36 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
215 printf("\n"); 214 printf("\n");
216} 215}
217 216
217/* Returns true if alert should be raised based on the range, false otherwise */
218bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
219 bool is_inside = false;
220
221 if (!my_range.end_infinity && !my_range.start_infinity) {
222 // range: .........|---inside---|...........
223 // value
224 is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) &&
225 (cmp_perfdata_value(value, my_range.end) <= 0));
226 } else if (!my_range.start_infinity && my_range.end_infinity) {
227 // range: .........|---inside---------
228 // value
229 is_inside = (cmp_perfdata_value(value, my_range.start) >= 0);
230 } else if (my_range.start_infinity && !my_range.end_infinity) {
231 // range: -inside--------|....................
232 // value
233 is_inside = (cmp_perfdata_value(value, my_range.end) == -1);
234 } else {
235 // range from -inf to inf, so always inside
236 is_inside = true;
237 }
238
239 if ((is_inside && my_range.alert_on_inside_range == INSIDE) ||
240 (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
241 return true;
242 }
243
244 return false;
245}
246
218/* Returns true if alert should be raised based on the range */ 247/* Returns true if alert should be raised based on the range */
219bool check_range(double value, range *my_range) { 248bool check_range(double value, range *my_range) {
220 bool no = false; 249 bool no = false;
@@ -225,38 +254,38 @@ bool check_range(double value, range *my_range) {
225 yes = false; 254 yes = false;
226 } 255 }
227 256
228 if (my_range->end_infinity == false && my_range->start_infinity == false) { 257 if (!my_range->end_infinity && !my_range->start_infinity) {
229 if ((my_range->start <= value) && (value <= my_range->end)) { 258 if ((my_range->start <= value) && (value <= my_range->end)) {
230 return no; 259 return no;
231 } else {
232 return yes;
233 } 260 }
234 } else if (my_range->start_infinity == false && my_range->end_infinity == true) { 261 return yes;
262 }
263
264 if (!my_range->start_infinity && my_range->end_infinity) {
235 if (my_range->start <= value) { 265 if (my_range->start <= value) {
236 return no; 266 return no;
237 } else {
238 return yes;
239 } 267 }
240 } else if (my_range->start_infinity == true && my_range->end_infinity == false) { 268 return yes;
269 }
270
271 if (my_range->start_infinity && !my_range->end_infinity) {
241 if (value <= my_range->end) { 272 if (value <= my_range->end) {
242 return no; 273 return no;
243 } else {
244 return yes;
245 } 274 }
246 } else { 275 return yes;
247 return no;
248 } 276 }
277 return no;
249} 278}
250 279
251/* Returns status */ 280/* Returns status */
252int get_status(double value, thresholds *my_thresholds) { 281mp_state_enum get_status(double value, thresholds *my_thresholds) {
253 if (my_thresholds->critical != NULL) { 282 if (my_thresholds->critical != NULL) {
254 if (check_range(value, my_thresholds->critical) == true) { 283 if (check_range(value, my_thresholds->critical)) {
255 return STATE_CRITICAL; 284 return STATE_CRITICAL;
256 } 285 }
257 } 286 }
258 if (my_thresholds->warning != NULL) { 287 if (my_thresholds->warning != NULL) {
259 if (check_range(value, my_thresholds->warning) == true) { 288 if (check_range(value, my_thresholds->warning)) {
260 return STATE_WARNING; 289 return STATE_WARNING;
261 } 290 }
262 } 291 }
@@ -265,31 +294,31 @@ int get_status(double value, thresholds *my_thresholds) {
265 294
266char *np_escaped_string(const char *string) { 295char *np_escaped_string(const char *string) {
267 char *data; 296 char *data;
268 int i, j = 0; 297 int write_index = 0;
269 data = strdup(string); 298 data = strdup(string);
270 for (i = 0; data[i]; i++) { 299 for (int i = 0; data[i]; i++) {
271 if (data[i] == '\\') { 300 if (data[i] == '\\') {
272 switch (data[++i]) { 301 switch (data[++i]) {
273 case 'n': 302 case 'n':
274 data[j++] = '\n'; 303 data[write_index++] = '\n';
275 break; 304 break;
276 case 'r': 305 case 'r':
277 data[j++] = '\r'; 306 data[write_index++] = '\r';
278 break; 307 break;
279 case 't': 308 case 't':
280 data[j++] = '\t'; 309 data[write_index++] = '\t';
281 break; 310 break;
282 case '\\': 311 case '\\':
283 data[j++] = '\\'; 312 data[write_index++] = '\\';
284 break; 313 break;
285 default: 314 default:
286 data[j++] = data[i]; 315 data[write_index++] = data[i];
287 } 316 }
288 } else { 317 } else {
289 data[j++] = data[i]; 318 data[write_index++] = data[i];
290 } 319 }
291 } 320 }
292 data[j] = '\0'; 321 data[write_index] = '\0';
293 return data; 322 return data;
294} 323}
295 324
@@ -302,38 +331,43 @@ int np_check_if_root(void) { return (geteuid() == 0); }
302 * data strings. 331 * data strings.
303 */ 332 */
304char *np_extract_value(const char *varlist, const char *name, char sep) { 333char *np_extract_value(const char *varlist, const char *name, char sep) {
305 char *tmp = NULL, *value = NULL; 334 char *tmp = NULL;
306 int i; 335 char *value = NULL;
307 336
308 while (1) { 337 while (true) {
309 /* Strip any leading space */ 338 /* Strip any leading space */
310 for (; isspace(varlist[0]); varlist++) 339 for (; isspace(varlist[0]); varlist++) {
311 ; 340 ;
341 }
312 342
313 if (strncmp(name, varlist, strlen(name)) == 0) { 343 if (strncmp(name, varlist, strlen(name)) == 0) {
314 varlist += strlen(name); 344 varlist += strlen(name);
315 /* strip trailing spaces */ 345 /* strip trailing spaces */
316 for (; isspace(varlist[0]); varlist++) 346 for (; isspace(varlist[0]); varlist++) {
317 ; 347 ;
348 }
318 349
319 if (varlist[0] == '=') { 350 if (varlist[0] == '=') {
320 /* We matched the key, go past the = sign */ 351 /* We matched the key, go past the = sign */
321 varlist++; 352 varlist++;
322 /* strip leading spaces */ 353 /* strip leading spaces */
323 for (; isspace(varlist[0]); varlist++) 354 for (; isspace(varlist[0]); varlist++) {
324 ; 355 ;
356 }
325 357
326 if ((tmp = index(varlist, sep))) { 358 if ((tmp = index(varlist, sep))) {
327 /* Value is delimited by a comma */ 359 /* Value is delimited by a comma */
328 if (tmp - varlist == 0) 360 if (tmp - varlist == 0) {
329 continue; 361 continue;
330 value = (char *)calloc(1, tmp - varlist + 1); 362 }
331 strncpy(value, varlist, tmp - varlist); 363 value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1));
364 strncpy(value, varlist, (unsigned long)(tmp - varlist));
332 value[tmp - varlist] = '\0'; 365 value[tmp - varlist] = '\0';
333 } else { 366 } else {
334 /* Value is delimited by a \0 */ 367 /* Value is delimited by a \0 */
335 if (strlen(varlist) == 0) 368 if (strlen(varlist) == 0) {
336 continue; 369 continue;
370 }
337 value = (char *)calloc(1, strlen(varlist) + 1); 371 value = (char *)calloc(1, strlen(varlist) + 1);
338 strncpy(value, varlist, strlen(varlist)); 372 strncpy(value, varlist, strlen(varlist));
339 value[strlen(varlist)] = '\0'; 373 value[strlen(varlist)] = '\0';
@@ -351,14 +385,16 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
351 } 385 }
352 386
353 /* Clean-up trailing spaces/newlines */ 387 /* Clean-up trailing spaces/newlines */
354 if (value) 388 if (value) {
355 for (i = strlen(value) - 1; isspace(value[i]); i--) 389 for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) {
356 value[i] = '\0'; 390 value[i] = '\0';
391 }
392 }
357 393
358 return value; 394 return value;
359} 395}
360 396
361const char *state_text(int result) { 397const char *state_text(mp_state_enum result) {
362 switch (result) { 398 switch (result) {
363 case STATE_OK: 399 case STATE_OK:
364 return "OK"; 400 return "OK";
@@ -378,343 +414,17 @@ const char *state_text(int result) {
378 * return the corresponding STATE_ value or ERROR) 414 * return the corresponding STATE_ value or ERROR)
379 */ 415 */
380int mp_translate_state(char *state_text) { 416int mp_translate_state(char *state_text) {
381 if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) 417 if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) {
382 return STATE_OK; 418 return STATE_OK;
383 if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1"))
384 return STATE_WARNING;
385 if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2"))
386 return STATE_CRITICAL;
387 if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3"))
388 return STATE_UNKNOWN;
389 return ERROR;
390}
391
392/*
393 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
394 * hopefully a unique key per service/plugin invocation. Use the extra-opts
395 * parse of argv, so that uniqueness in parameters are reflected there.
396 */
397char *_np_state_generate_key() {
398 int i;
399 char **argv = this_monitoring_plugin->argv;
400 char keyname[41];
401 char *p = NULL;
402
403 unsigned char result[256];
404
405#ifdef USE_OPENSSL
406 /*
407 * This code path is chosen if openssl is available (which should be the most common
408 * scenario). Alternatively, the gnulib implementation/
409 *
410 */
411 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
412
413 EVP_DigestInit(ctx, EVP_sha256());
414
415 for (i = 0; i < this_monitoring_plugin->argc; i++) {
416 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
417 }
418
419 EVP_DigestFinal(ctx, result, NULL);
420#else
421
422 struct sha256_ctx ctx;
423
424 for (i = 0; i < this_monitoring_plugin->argc; i++) {
425 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
426 }
427
428 sha256_finish_ctx(&ctx, result);
429#endif // FOUNDOPENSSL
430
431 for (i = 0; i < 20; ++i) {
432 sprintf(&keyname[2 * i], "%02x", result[i]);
433 }
434
435 keyname[40] = '\0';
436
437 p = strdup(keyname);
438 if (p == NULL) {
439 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
440 }
441 return p;
442}
443
444void _cleanup_state_data() {
445 if (this_monitoring_plugin->state->state_data != NULL) {
446 np_free(this_monitoring_plugin->state->state_data->data);
447 np_free(this_monitoring_plugin->state->state_data);
448 }
449}
450
451/*
452 * Internal function. Returns either:
453 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
454 * statically compiled shared state directory
455 */
456char *_np_state_calculate_location_prefix() {
457 char *env_dir;
458
459 /* Do not allow passing MP_STATE_PATH in setuid plugins
460 * for security reasons */
461 if (!mp_suid()) {
462 env_dir = getenv("MP_STATE_PATH");
463 if (env_dir && env_dir[0] != '\0')
464 return env_dir;
465 /* This is the former ENV, for backward-compatibility */
466 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
467 if (env_dir && env_dir[0] != '\0')
468 return env_dir;
469 }
470
471 return NP_STATE_DIR_PREFIX;
472}
473
474/*
475 * Initiatializer for state routines.
476 * Sets variables. Generates filename. Returns np_state_key. die with
477 * UNKNOWN if exception
478 */
479void np_enable_state(char *keyname, int expected_data_version) {
480 state_key *this_state = NULL;
481 char *temp_filename = NULL;
482 char *temp_keyname = NULL;
483 char *p = NULL;
484 int ret;
485
486 if (this_monitoring_plugin == NULL)
487 die(STATE_UNKNOWN, _("This requires np_init to be called"));
488
489 this_state = (state_key *)calloc(1, sizeof(state_key));
490 if (this_state == NULL)
491 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
492
493 if (keyname == NULL) {
494 temp_keyname = _np_state_generate_key();
495 } else {
496 temp_keyname = strdup(keyname);
497 if (temp_keyname == NULL)
498 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
499 } 419 }
500 /* Die if invalid characters used for keyname */ 420 if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) {
501 p = temp_keyname; 421 return STATE_WARNING;
502 while (*p != '\0') {
503 if (!(isalnum(*p) || *p == '_')) {
504 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
505 }
506 p++;
507 }
508 this_state->name = temp_keyname;
509 this_state->plugin_name = this_monitoring_plugin->plugin_name;
510 this_state->data_version = expected_data_version;
511 this_state->state_data = NULL;
512
513 /* Calculate filename */
514 ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
515 this_monitoring_plugin->plugin_name, this_state->name);
516 if (ret < 0)
517 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
518
519 this_state->_filename = temp_filename;
520
521 this_monitoring_plugin->state = this_state;
522}
523
524/*
525 * Will return NULL if no data is available (first run). If key currently
526 * exists, read data. If state file format version is not expected, return
527 * as if no data. Get state data version number and compares to expected.
528 * If numerically lower, then return as no previous state. die with UNKNOWN
529 * if exceptional error.
530 */
531state_data *np_state_read() {
532 state_data *this_state_data = NULL;
533 FILE *statefile;
534 bool rc = false;
535
536 if (this_monitoring_plugin == NULL)
537 die(STATE_UNKNOWN, _("This requires np_init to be called"));
538
539 /* Open file. If this fails, no previous state found */
540 statefile = fopen(this_monitoring_plugin->state->_filename, "r");
541 if (statefile != NULL) {
542
543 this_state_data = (state_data *)calloc(1, sizeof(state_data));
544 if (this_state_data == NULL)
545 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
546
547 this_state_data->data = NULL;
548 this_monitoring_plugin->state->state_data = this_state_data;
549
550 rc = _np_state_read_file(statefile);
551
552 fclose(statefile);
553 }
554
555 if (!rc) {
556 _cleanup_state_data();
557 }
558
559 return this_monitoring_plugin->state->state_data;
560}
561
562/*
563 * Read the state file
564 */
565bool _np_state_read_file(FILE *f) {
566 bool status = false;
567 size_t pos;
568 char *line;
569 int i;
570 int failure = 0;
571 time_t current_time, data_time;
572 enum {
573 STATE_FILE_VERSION,
574 STATE_DATA_VERSION,
575 STATE_DATA_TIME,
576 STATE_DATA_TEXT,
577 STATE_DATA_END
578 } expected = STATE_FILE_VERSION;
579
580 time(&current_time);
581
582 /* Note: This introduces a limit of 1024 bytes in the string data */
583 line = (char *)calloc(1, 1024);
584 if (line == NULL)
585 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
586
587 while (!failure && (fgets(line, 1024, f)) != NULL) {
588 pos = strlen(line);
589 if (line[pos - 1] == '\n') {
590 line[pos - 1] = '\0';
591 }
592
593 if (line[0] == '#')
594 continue;
595
596 switch (expected) {
597 case STATE_FILE_VERSION:
598 i = atoi(line);
599 if (i != NP_STATE_FORMAT_VERSION)
600 failure++;
601 else
602 expected = STATE_DATA_VERSION;
603 break;
604 case STATE_DATA_VERSION:
605 i = atoi(line);
606 if (i != this_monitoring_plugin->state->data_version)
607 failure++;
608 else
609 expected = STATE_DATA_TIME;
610 break;
611 case STATE_DATA_TIME:
612 /* If time > now, error */
613 data_time = strtoul(line, NULL, 10);
614 if (data_time > current_time)
615 failure++;
616 else {
617 this_monitoring_plugin->state->state_data->time = data_time;
618 expected = STATE_DATA_TEXT;
619 }
620 break;
621 case STATE_DATA_TEXT:
622 this_monitoring_plugin->state->state_data->data = strdup(line);
623 if (this_monitoring_plugin->state->state_data->data == NULL)
624 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
625 expected = STATE_DATA_END;
626 status = true;
627 break;
628 case STATE_DATA_END:;
629 }
630 }
631
632 np_free(line);
633 return status;
634}
635
636/*
637 * If time=NULL, use current time. Create state file, with state format
638 * version, default text. Writes version, time, and data. Avoid locking
639 * problems - use mv to write and then swap. Possible loss of state data if
640 * two things writing to same key at same time.
641 * Will die with UNKNOWN if errors
642 */
643void np_state_write_string(time_t data_time, char *data_string) {
644 FILE *fp;
645 char *temp_file = NULL;
646 int fd = 0, result = 0;
647 time_t current_time;
648 char *directories = NULL;
649 char *p = NULL;
650
651 if (data_time == 0)
652 time(&current_time);
653 else
654 current_time = data_time;
655
656 /* If file doesn't currently exist, create directories */
657 if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
658 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
659 if (result < 0)
660 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
661
662 for (p = directories + 1; *p; p++) {
663 if (*p == '/') {
664 *p = '\0';
665 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
666 /* Can't free this! Otherwise error message is wrong! */
667 /* np_free(directories); */
668 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
669 }
670 *p = '/';
671 }
672 }
673 np_free(directories);
674 }
675
676 result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
677 if (result < 0)
678 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
679
680 if ((fd = mkstemp(temp_file)) == -1) {
681 np_free(temp_file);
682 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
683 }
684
685 fp = (FILE *)fdopen(fd, "w");
686 if (fp == NULL) {
687 close(fd);
688 unlink(temp_file);
689 np_free(temp_file);
690 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
691 } 422 }
692 423 if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) {
693 fprintf(fp, "# NP State file\n"); 424 return STATE_CRITICAL;
694 fprintf(fp, "%d\n", NP_STATE_FORMAT_VERSION);
695 fprintf(fp, "%d\n", this_monitoring_plugin->state->data_version);
696 fprintf(fp, "%lu\n", current_time);
697 fprintf(fp, "%s\n", data_string);
698
699 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
700
701 fflush(fp);
702
703 result = fclose(fp);
704
705 fsync(fd);
706
707 if (result != 0) {
708 unlink(temp_file);
709 np_free(temp_file);
710 die(STATE_UNKNOWN, _("Error writing temp file"));
711 } 425 }
712 426 if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) {
713 if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) { 427 return STATE_UNKNOWN;
714 unlink(temp_file);
715 np_free(temp_file);
716 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
717 } 428 }
718 429 return ERROR;
719 np_free(temp_file);
720} 430}
diff --git a/lib/utils_base.h b/lib/utils_base.h
index a209cb6d..27884bf0 100644
--- a/lib/utils_base.h
+++ b/lib/utils_base.h
@@ -2,6 +2,13 @@
2#define _UTILS_BASE_ 2#define _UTILS_BASE_
3/* Header file for Monitoring Plugins utils_base.c */ 3/* Header file for Monitoring Plugins utils_base.c */
4 4
5#include "../config.h"
6#include <time.h>
7
8#include "./perfdata.h"
9#include "./thresholds.h"
10#include "states.h"
11
5#ifndef USE_OPENSSL 12#ifndef USE_OPENSSL
6# include "sha256.h" 13# include "sha256.h"
7#endif 14#endif
@@ -19,39 +26,8 @@
19#define OUTSIDE 0 26#define OUTSIDE 0
20#define INSIDE 1 27#define INSIDE 1
21 28
22typedef struct range_struct {
23 double start;
24 bool start_infinity;
25 double end;
26 int end_infinity;
27 int alert_on; /* OUTSIDE (default) or INSIDE */
28 char *text; /* original unparsed text input */
29} range;
30
31typedef struct thresholds_struct {
32 range *warning;
33 range *critical;
34} thresholds;
35
36#define NP_STATE_FORMAT_VERSION 1
37
38typedef struct state_data_struct {
39 time_t time;
40 void *data;
41 int length; /* Of binary data */
42} state_data;
43
44typedef struct state_key_struct {
45 char *name;
46 char *plugin_name;
47 int data_version;
48 char *_filename;
49 state_data *state_data;
50} state_key;
51
52typedef struct np_struct { 29typedef struct np_struct {
53 char *plugin_name; 30 char *plugin_name;
54 state_key *state;
55 int argc; 31 int argc;
56 char **argv; 32 char **argv;
57} monitoring_plugin; 33} monitoring_plugin;
@@ -61,10 +37,11 @@ int _set_thresholds(thresholds **, char *, char *);
61void set_thresholds(thresholds **, char *, char *); 37void set_thresholds(thresholds **, char *, char *);
62void print_thresholds(const char *, thresholds *); 38void print_thresholds(const char *, thresholds *);
63bool check_range(double, range *); 39bool check_range(double, range *);
64int get_status(double, thresholds *); 40bool mp_check_range(mp_perfdata_value, mp_range);
41mp_state_enum get_status(double, thresholds *);
65 42
66/* Handle timeouts */ 43/* Handle timeouts */
67extern int timeout_state; 44extern mp_state_enum timeout_state;
68extern unsigned int timeout_interval; 45extern unsigned int timeout_interval;
69 46
70/* All possible characters in a threshold range */ 47/* All possible characters in a threshold range */
@@ -106,13 +83,9 @@ char *np_extract_value(const char *, const char *, char);
106 */ 83 */
107int mp_translate_state(char *); 84int mp_translate_state(char *);
108 85
109void np_enable_state(char *, int);
110state_data *np_state_read();
111void np_state_write_string(time_t, char *);
112
113void np_init(char *, int argc, char **argv); 86void np_init(char *, int argc, char **argv);
114void np_set_args(int argc, char **argv); 87void np_set_args(int argc, char **argv);
115void np_cleanup(); 88void np_cleanup(void);
116const char *state_text(int); 89const char *state_text(mp_state_enum);
117 90
118#endif /* _UTILS_BASE_ */ 91#endif /* _UTILS_BASE_ */
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c
index 18350ac0..35b83297 100644
--- a/lib/utils_cmd.c
+++ b/lib/utils_cmd.c
@@ -40,7 +40,6 @@
40 40
41/** includes **/ 41/** includes **/
42#include "common.h" 42#include "common.h"
43#include "utils.h"
44#include "utils_cmd.h" 43#include "utils_cmd.h"
45/* This variable must be global, since there's no way the caller 44/* This variable must be global, since there's no way the caller
46 * can forcibly slay a dead or ungainly running program otherwise. 45 * can forcibly slay a dead or ungainly running program otherwise.
@@ -62,16 +61,13 @@ static pid_t *_cmd_pids = NULL;
62# include <sys/wait.h> 61# include <sys/wait.h>
63#endif 62#endif
64 63
65/* used in _cmd_open to pass the environment to commands */
66extern char **environ;
67
68/** macros **/ 64/** macros **/
69#ifndef WEXITSTATUS 65#ifndef WEXITSTATUS
70# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 66# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
71#endif 67#endif
72 68
73#ifndef WIFEXITED 69#ifndef WIFEXITED
74# define WIFEXITED(stat_val) (((stat_val)&255) == 0) 70# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
75#endif 71#endif
76 72
77/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 73/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
@@ -80,14 +76,12 @@ extern char **environ;
80#endif 76#endif
81 77
82/** prototypes **/ 78/** prototypes **/
83static int _cmd_open(char *const *, int *, int *) __attribute__((__nonnull__(1, 2, 3))); 79static int _cmd_open(char *const *argv, int *pfd, int *pfderr)
84 80 __attribute__((__nonnull__(1, 2, 3)));
85static int _cmd_fetch_output(int, output *, int) __attribute__((__nonnull__(2)));
86 81
87static int _cmd_close(int); 82static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) __attribute__((__nonnull__(2)));
88 83
89/* prototype imported from utils.h */ 84static int _cmd_close(int fileDescriptor);
90extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
91 85
92/* this function is NOT async-safe. It is exported so multithreaded 86/* this function is NOT async-safe. It is exported so multithreaded
93 * plugins (or other apps) can call it prior to running any commands 87 * plugins (or other apps) can call it prior to running any commands
@@ -103,26 +97,29 @@ void cmd_init(void) {
103 maxfd = MAXFD_LIMIT; 97 maxfd = MAXFD_LIMIT;
104 } 98 }
105 99
106 if (!_cmd_pids) 100 if (!_cmd_pids) {
107 _cmd_pids = calloc(maxfd, sizeof(pid_t)); 101 _cmd_pids = calloc(maxfd, sizeof(pid_t));
102 }
108} 103}
109 104
110/* Start running a command, array style */ 105/* Start running a command, array style */
111static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { 106static int _cmd_open(char *const *argv, int *pfd, int *pfderr) {
112 pid_t pid;
113#ifdef RLIMIT_CORE 107#ifdef RLIMIT_CORE
114 struct rlimit limit; 108 struct rlimit limit;
115#endif 109#endif
116 110
117 int i = 0; 111 int i = 0;
118 112
119 if (!_cmd_pids) 113 if (!_cmd_pids) {
120 CMD_INIT; 114 CMD_INIT;
115 }
121 116
122 setenv("LC_ALL", "C", 1); 117 setenv("LC_ALL", "C", 1);
123 118
124 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) 119 pid_t pid;
120 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) {
125 return -1; /* errno set by the failing function */ 121 return -1; /* errno set by the failing function */
122 }
126 123
127 /* child runs exceve() and _exit. */ 124 /* child runs exceve() and _exit. */
128 if (pid == 0) { 125 if (pid == 0) {
@@ -147,9 +144,11 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) {
147 * This is executed in a separate address space (pure child), 144 * This is executed in a separate address space (pure child),
148 * so we don't have to worry about async safety */ 145 * so we don't have to worry about async safety */
149 long maxfd = mp_open_max(); 146 long maxfd = mp_open_max();
150 for (i = 0; i < maxfd; i++) 147 for (i = 0; i < maxfd; i++) {
151 if (_cmd_pids[i] > 0) 148 if (_cmd_pids[i] > 0) {
152 close(i); 149 close(i);
150 }
151 }
153 152
154 execve(argv[0], argv, environ); 153 execve(argv[0], argv, environ);
155 _exit(STATE_UNKNOWN); 154 _exit(STATE_UNKNOWN);
@@ -166,87 +165,94 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) {
166 return pfd[0]; 165 return pfd[0];
167} 166}
168 167
169static int _cmd_close(int fd) { 168static int _cmd_close(int fileDescriptor) {
170 int status;
171 pid_t pid; 169 pid_t pid;
172 170
173 /* make sure the provided fd was opened */ 171 /* make sure the provided fd was opened */
174 long maxfd = mp_open_max(); 172 long maxfd = mp_open_max();
175 if (fd < 0 || fd > maxfd || !_cmd_pids || (pid = _cmd_pids[fd]) == 0) 173 if (fileDescriptor < 0 || fileDescriptor > maxfd || !_cmd_pids ||
174 (pid = _cmd_pids[fileDescriptor]) == 0) {
176 return -1; 175 return -1;
176 }
177 177
178 _cmd_pids[fd] = 0; 178 _cmd_pids[fileDescriptor] = 0;
179 if (close(fd) == -1) 179 if (close(fileDescriptor) == -1) {
180 return -1; 180 return -1;
181 }
181 182
182 /* EINTR is ok (sort of), everything else is bad */ 183 /* EINTR is ok (sort of), everything else is bad */
183 while (waitpid(pid, &status, 0) < 0) 184 int status;
184 if (errno != EINTR) 185 while (waitpid(pid, &status, 0) < 0) {
186 if (errno != EINTR) {
185 return -1; 187 return -1;
188 }
189 }
186 190
187 /* return child's termination status */ 191 /* return child's termination status */
188 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 192 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
189} 193}
190 194
191static int _cmd_fetch_output(int fd, output *op, int flags) { 195static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) {
192 size_t len = 0, i = 0, lineno = 0;
193 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
194 char *buf = NULL;
195 int ret;
196 char tmpbuf[4096]; 196 char tmpbuf[4096];
197 197 cmd_output->buf = NULL;
198 op->buf = NULL; 198 cmd_output->buflen = 0;
199 op->buflen = 0; 199 ssize_t ret;
200 while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { 200 while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) {
201 len = (size_t)ret; 201 size_t len = (size_t)ret;
202 op->buf = realloc(op->buf, op->buflen + len + 1); 202 cmd_output->buf = realloc(cmd_output->buf, cmd_output->buflen + len + 1);
203 memcpy(op->buf + op->buflen, tmpbuf, len); 203 memcpy(cmd_output->buf + cmd_output->buflen, tmpbuf, len);
204 op->buflen += len; 204 cmd_output->buflen += len;
205 i++;
206 } 205 }
207 206
208 if (ret < 0) { 207 if (ret < 0) {
209 printf("read() returned %d: %s\n", ret, strerror(errno)); 208 printf("read() returned %zd: %s\n", ret, strerror(errno));
210 return ret; 209 return ret;
211 } 210 }
212 211
213 /* some plugins may want to keep output unbroken, and some commands 212 /* some plugins may want to keep output unbroken, and some commands
214 * will yield no output, so return here for those */ 213 * will yield no output, so return here for those */
215 if (flags & CMD_NO_ARRAYS || !op->buf || !op->buflen) 214 if (flags & CMD_NO_ARRAYS || !cmd_output->buf || !cmd_output->buflen) {
216 return op->buflen; 215 return cmd_output->buflen;
216 }
217 217
218 /* and some may want both */ 218 /* and some may want both */
219 char *buf = NULL;
219 if (flags & CMD_NO_ASSOC) { 220 if (flags & CMD_NO_ASSOC) {
220 buf = malloc(op->buflen); 221 buf = malloc(cmd_output->buflen);
221 memcpy(buf, op->buf, op->buflen); 222 memcpy(buf, cmd_output->buf, cmd_output->buflen);
222 } else 223 } else {
223 buf = op->buf; 224 buf = cmd_output->buf;
224 225 }
225 op->line = NULL; 226
226 op->lens = NULL; 227 cmd_output->line = NULL;
227 i = 0; 228 cmd_output->lens = NULL;
228 while (i < op->buflen) { 229 size_t i = 0;
230 size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
231 size_t rsf = 6;
232 size_t lineno = 0;
233 while (i < cmd_output->buflen) {
229 /* make sure we have enough memory */ 234 /* make sure we have enough memory */
230 if (lineno >= ary_size) { 235 if (lineno >= ary_size) {
231 /* ary_size must never be zero */ 236 /* ary_size must never be zero */
232 do { 237 do {
233 ary_size = op->buflen >> --rsf; 238 ary_size = cmd_output->buflen >> --rsf;
234 } while (!ary_size); 239 } while (!ary_size);
235 240
236 op->line = realloc(op->line, ary_size * sizeof(char *)); 241 cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *));
237 op->lens = realloc(op->lens, ary_size * sizeof(size_t)); 242 cmd_output->lens = realloc(cmd_output->lens, ary_size * sizeof(size_t));
238 } 243 }
239 244
240 /* set the pointer to the string */ 245 /* set the pointer to the string */
241 op->line[lineno] = &buf[i]; 246 cmd_output->line[lineno] = &buf[i];
242 247
243 /* hop to next newline or end of buffer */ 248 /* hop to next newline or end of buffer */
244 while (buf[i] != '\n' && i < op->buflen) 249 while (buf[i] != '\n' && i < cmd_output->buflen) {
245 i++; 250 i++;
251 }
246 buf[i] = '\0'; 252 buf[i] = '\0';
247 253
248 /* calculate the string length using pointer difference */ 254 /* calculate the string length using pointer difference */
249 op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno]; 255 cmd_output->lens[lineno] = (size_t)&buf[i] - (size_t)cmd_output->line[lineno];
250 256
251 lineno++; 257 lineno++;
252 i++; 258 i++;
@@ -256,41 +262,42 @@ static int _cmd_fetch_output(int fd, output *op, int flags) {
256} 262}
257 263
258int cmd_run(const char *cmdstring, output *out, output *err, int flags) { 264int cmd_run(const char *cmdstring, output *out, output *err, int flags) {
259 int i = 0, argc; 265 if (cmdstring == NULL) {
260 size_t cmdlen;
261 char **argv = NULL;
262 char *cmd = NULL;
263 char *str = NULL;
264
265 if (cmdstring == NULL)
266 return -1; 266 return -1;
267 }
267 268
268 /* initialize the structs */ 269 /* initialize the structs */
269 if (out) 270 if (out) {
270 memset(out, 0, sizeof(output)); 271 memset(out, 0, sizeof(output));
271 if (err) 272 }
273 if (err) {
272 memset(err, 0, sizeof(output)); 274 memset(err, 0, sizeof(output));
275 }
273 276
274 /* make copy of command string so strtok() doesn't silently modify it */ 277 /* make copy of command string so strtok() doesn't silently modify it */
275 /* (the calling program may want to access it later) */ 278 /* (the calling program may want to access it later) */
276 cmdlen = strlen(cmdstring); 279 size_t cmdlen = strlen(cmdstring);
277 if ((cmd = malloc(cmdlen + 1)) == NULL) 280 char *cmd = NULL;
281 if ((cmd = malloc(cmdlen + 1)) == NULL) {
278 return -1; 282 return -1;
283 }
279 memcpy(cmd, cmdstring, cmdlen); 284 memcpy(cmd, cmdstring, cmdlen);
280 cmd[cmdlen] = '\0'; 285 cmd[cmdlen] = '\0';
281 286
282 /* This is not a shell, so we don't handle "???" */ 287 /* This is not a shell, so we don't handle "???" */
283 if (strstr(cmdstring, "\"")) 288 if (strstr(cmdstring, "\"")) {
284 return -1; 289 return -1;
290 }
285 291
286 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 292 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
287 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) 293 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
288 return -1; 294 return -1;
295 }
289 296
290 /* each arg must be whitespace-separated, so args can be a maximum 297 /* each arg must be whitespace-separated, so args can be a maximum
291 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ 298 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
292 argc = (cmdlen >> 1) + 2; 299 int argc = (cmdlen >> 1) + 2;
293 argv = calloc((size_t)argc, sizeof(char *)); 300 char **argv = calloc((size_t)argc, sizeof(char *));
294 301
295 if (argv == NULL) { 302 if (argv == NULL) {
296 printf("%s\n", _("Could not malloc argv array in popen()")); 303 printf("%s\n", _("Could not malloc argv array in popen()"));
@@ -298,14 +305,16 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) {
298 } 305 }
299 306
300 /* get command arguments (stupidly, but fairly quickly) */ 307 /* get command arguments (stupidly, but fairly quickly) */
308 int i = 0;
301 while (cmd) { 309 while (cmd) {
302 str = cmd; 310 char *str = cmd;
303 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ 311 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
304 312
305 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ 313 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
306 str++; 314 str++;
307 if (!strstr(str, "'")) 315 if (!strstr(str, "'")) {
308 return -1; /* balanced? */ 316 return -1; /* balanced? */
317 }
309 cmd = 1 + strstr(str, "'"); 318 cmd = 1 + strstr(str, "'");
310 str[strcspn(str, "'")] = 0; 319 str[strcspn(str, "'")] = 0;
311 } else { 320 } else {
@@ -317,8 +326,9 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) {
317 } 326 }
318 } 327 }
319 328
320 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) 329 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
321 cmd = NULL; 330 cmd = NULL;
331 }
322 332
323 argv[i++] = str; 333 argv[i++] = str;
324 } 334 }
@@ -327,53 +337,65 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) {
327} 337}
328 338
329int cmd_run_array(char *const *argv, output *out, output *err, int flags) { 339int cmd_run_array(char *const *argv, output *out, output *err, int flags) {
330 int fd, pfd_out[2], pfd_err[2];
331
332 /* initialize the structs */ 340 /* initialize the structs */
333 if (out) 341 if (out) {
334 memset(out, 0, sizeof(output)); 342 memset(out, 0, sizeof(output));
335 if (err) 343 }
344 if (err) {
336 memset(err, 0, sizeof(output)); 345 memset(err, 0, sizeof(output));
346 }
337 347
338 if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) 348 int fd;
349 int pfd_out[2];
350 int pfd_err[2];
351 if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) {
339 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]); 352 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]);
353 }
340 354
341 if (out) 355 if (out) {
342 out->lines = _cmd_fetch_output(pfd_out[0], out, flags); 356 out->lines = _cmd_fetch_output(pfd_out[0], out, flags);
343 if (err) 357 }
358 if (err) {
344 err->lines = _cmd_fetch_output(pfd_err[0], err, flags); 359 err->lines = _cmd_fetch_output(pfd_err[0], err, flags);
360 }
345 361
346 return _cmd_close(fd); 362 return _cmd_close(fd);
347} 363}
348 364
349int cmd_file_read(char *filename, output *out, int flags) { 365int cmd_file_read(const char *filename, output *out, int flags) {
350 int fd; 366 int fd;
351 if (out) 367 if (out) {
352 memset(out, 0, sizeof(output)); 368 memset(out, 0, sizeof(output));
369 }
353 370
354 if ((fd = open(filename, O_RDONLY)) == -1) { 371 if ((fd = open(filename, O_RDONLY)) == -1) {
355 die(STATE_UNKNOWN, _("Error opening %s: %s"), filename, strerror(errno)); 372 die(STATE_UNKNOWN, _("Error opening %s: %s"), filename, strerror(errno));
356 } 373 }
357 374
358 if (out) 375 if (out) {
359 out->lines = _cmd_fetch_output(fd, out, flags); 376 out->lines = _cmd_fetch_output(fd, out, flags);
377 }
360 378
361 if (close(fd) == -1) 379 if (close(fd) == -1) {
362 die(STATE_UNKNOWN, _("Error closing %s: %s"), filename, strerror(errno)); 380 die(STATE_UNKNOWN, _("Error closing %s: %s"), filename, strerror(errno));
381 }
363 382
364 return 0; 383 return 0;
365} 384}
366 385
367void timeout_alarm_handler(int signo) { 386void timeout_alarm_handler(int signo) {
368 if (signo == SIGALRM) { 387 if (signo == SIGALRM) {
369 printf(_("%s - Plugin timed out after %d seconds\n"), state_text(timeout_state), timeout_interval); 388 printf(_("%s - Plugin timed out after %d seconds\n"), state_text(timeout_state),
389 timeout_interval);
370 390
371 long maxfd = mp_open_max(); 391 long maxfd = mp_open_max();
372 if (_cmd_pids) 392 if (_cmd_pids) {
373 for (long int i = 0; i < maxfd; i++) { 393 for (long int i = 0; i < maxfd; i++) {
374 if (_cmd_pids[i] != 0) 394 if (_cmd_pids[i] != 0) {
375 kill(_cmd_pids[i], SIGKILL); 395 kill(_cmd_pids[i], SIGKILL);
396 }
376 } 397 }
398 }
377 399
378 exit(timeout_state); 400 exit(timeout_state);
379 } 401 }
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h
index d00069c9..3672cdc9 100644
--- a/lib/utils_cmd.h
+++ b/lib/utils_cmd.h
@@ -5,22 +5,22 @@
5 * Header file for Monitoring Plugins utils_cmd.c 5 * Header file for Monitoring Plugins utils_cmd.c
6 * 6 *
7 */ 7 */
8#include "../config.h"
9#include <stddef.h>
8 10
9/** types **/ 11/** types **/
10struct output { 12typedef struct {
11 char *buf; /* output buffer */ 13 char *buf; /* output buffer */
12 size_t buflen; /* output buffer content length */ 14 size_t buflen; /* output buffer content length */
13 char **line; /* array of lines (points to buf) */ 15 char **line; /* array of lines (points to buf) */
14 size_t *lens; /* string lengths */ 16 size_t *lens; /* string lengths */
15 size_t lines; /* lines of output */ 17 size_t lines; /* lines of output */
16}; 18} output;
17
18typedef struct output output;
19 19
20/** prototypes **/ 20/** prototypes **/
21int cmd_run(const char *, output *, output *, int); 21int cmd_run(const char *, output *, output *, int);
22int cmd_run_array(char *const *, output *, output *, int); 22int cmd_run_array(char *const *, output *, output *, int);
23int cmd_file_read(char *, output *, int); 23int cmd_file_read(const char *, output *, int);
24 24
25/* only multi-threaded plugins need to bother with this */ 25/* only multi-threaded plugins need to bother with this */
26void cmd_init(void); 26void cmd_init(void);
diff --git a/lib/utils_disk.c b/lib/utils_disk.c
deleted file mode 100644
index 2b761f5e..00000000
--- a/lib/utils_disk.c
+++ /dev/null
@@ -1,255 +0,0 @@
1/*****************************************************************************
2 *
3 * Library for check_disk
4 *
5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains utilities for check_disk. These are tested by libtap
11 *
12 *
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 *
27 *****************************************************************************/
28
29#include "common.h"
30#include "utils_disk.h"
31#include "gl/fsusage.h"
32#include <string.h>
33
34void np_add_name(struct name_list **list, const char *name) {
35 struct name_list *new_entry;
36 new_entry = (struct name_list *)malloc(sizeof *new_entry);
37 new_entry->name = (char *)name;
38 new_entry->next = *list;
39 *list = new_entry;
40}
41
42/* @brief Initialises a new regex at the begin of list via regcomp(3)
43 *
44 * @details if the regex fails to compile the error code of regcomp(3) is returned
45 * and list is not modified, otherwise list is modified to point to the new
46 * element
47 * @param list Pointer to a linked list of regex_list elements
48 * @param regex the string containing the regex which should be inserted into the list
49 * @param clags the cflags parameter for regcomp(3)
50 */
51int np_add_regex(struct regex_list **list, const char *regex, int cflags) {
52 struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry);
53
54 if (new_entry == NULL) {
55 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
56 }
57
58 int regcomp_result = regcomp(&new_entry->regex, regex, cflags);
59
60 if (!regcomp_result) {
61 // regcomp succeeded
62 new_entry->next = *list;
63 *list = new_entry;
64
65 return 0;
66 } else {
67 // regcomp failed
68 free(new_entry);
69
70 return regcomp_result;
71 }
72}
73
74/* Initialises a new parameter at the end of list */
75struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name) {
76 struct parameter_list *current = *list;
77 struct parameter_list *new_path;
78 new_path = (struct parameter_list *)malloc(sizeof *new_path);
79 new_path->name = (char *)malloc(strlen(name) + 1);
80 new_path->best_match = NULL;
81 new_path->name_next = NULL;
82 new_path->name_prev = NULL;
83 new_path->freespace_bytes = NULL;
84 new_path->freespace_units = NULL;
85 new_path->freespace_percent = NULL;
86 new_path->usedspace_bytes = NULL;
87 new_path->usedspace_units = NULL;
88 new_path->usedspace_percent = NULL;
89 new_path->usedinodes_percent = NULL;
90 new_path->freeinodes_percent = NULL;
91 new_path->group = NULL;
92 new_path->dfree_pct = -1;
93 new_path->dused_pct = -1;
94 new_path->total = 0;
95 new_path->available = 0;
96 new_path->available_to_root = 0;
97 new_path->used = 0;
98 new_path->dused_units = 0;
99 new_path->dfree_units = 0;
100 new_path->dtotal_units = 0;
101 new_path->inodes_total = 0;
102 new_path->inodes_free = 0;
103 new_path->inodes_free_to_root = 0;
104 new_path->inodes_used = 0;
105 new_path->dused_inodes_percent = 0;
106 new_path->dfree_inodes_percent = 0;
107
108 strcpy(new_path->name, name);
109
110 if (current == NULL) {
111 *list = new_path;
112 new_path->name_prev = NULL;
113 } else {
114 while (current->name_next) {
115 current = current->name_next;
116 }
117 current->name_next = new_path;
118 new_path->name_prev = current;
119 }
120 return new_path;
121}
122
123/* Delete a given parameter from list and return pointer to next element*/
124struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev) {
125 if (item == NULL) {
126 return NULL;
127 }
128 struct parameter_list *next;
129
130 if (item->name_next)
131 next = item->name_next;
132 else
133 next = NULL;
134
135 if (next)
136 next->name_prev = prev;
137
138 if (prev)
139 prev->name_next = next;
140
141 if (item->name) {
142 free(item->name);
143 }
144 free(item);
145
146 return next;
147}
148
149/* returns a pointer to the struct found in the list */
150struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) {
151 struct parameter_list *temp_list;
152 for (temp_list = list; temp_list; temp_list = temp_list->name_next) {
153 if (!strcmp(temp_list->name, name))
154 return temp_list;
155 }
156
157 return NULL;
158}
159
160void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) {
161 struct parameter_list *d;
162 for (d = desired; d; d = d->name_next) {
163 if (!d->best_match) {
164 struct mount_entry *me;
165 size_t name_len = strlen(d->name);
166 size_t best_match_len = 0;
167 struct mount_entry *best_match = NULL;
168 struct fs_usage fsp;
169
170 /* set best match if path name exactly matches a mounted device name */
171 for (me = mount_list; me; me = me->me_next) {
172 if (strcmp(me->me_devname, d->name) == 0) {
173 if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) {
174 best_match = me;
175 }
176 }
177 }
178
179 /* set best match by directory name if no match was found by devname */
180 if (!best_match) {
181 for (me = mount_list; me; me = me->me_next) {
182 size_t len = strlen(me->me_mountdir);
183 if ((!exact &&
184 (best_match_len <= len && len <= name_len && (len == 1 || strncmp(me->me_mountdir, d->name, len) == 0))) ||
185 (exact && strcmp(me->me_mountdir, d->name) == 0)) {
186 if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) {
187 best_match = me;
188 best_match_len = len;
189 }
190 }
191 }
192 }
193
194 if (best_match) {
195 d->best_match = best_match;
196 } else {
197 d->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */
198 }
199 }
200 }
201}
202
203/* Returns true if name is in list */
204bool np_find_name(struct name_list *list, const char *name) {
205 const struct name_list *n;
206
207 if (list == NULL || name == NULL) {
208 return false;
209 }
210 for (n = list; n; n = n->next) {
211 if (!strcmp(name, n->name)) {
212 return true;
213 }
214 }
215 return false;
216}
217
218/* Returns true if name is in list */
219bool np_find_regmatch(struct regex_list *list, const char *name) {
220 int len;
221 regmatch_t m;
222
223 if (name == NULL) {
224 return false;
225 }
226
227 len = strlen(name);
228
229 for (; list; list = list->next) {
230 /* Emulate a full match as if surrounded with ^( )$
231 by checking whether the match spans the whole name */
232 if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) {
233 return true;
234 }
235 }
236
237 return false;
238}
239
240bool np_seen_name(struct name_list *list, const char *name) {
241 const struct name_list *s;
242 for (s = list; s; s = s->next) {
243 if (!strcmp(s->name, name)) {
244 return true;
245 }
246 }
247 return false;
248}
249
250bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) {
251 if (regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0 || regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0) {
252 return true;
253 }
254 return false;
255}
diff --git a/lib/utils_disk.h b/lib/utils_disk.h
deleted file mode 100644
index c5e81dc1..00000000
--- a/lib/utils_disk.h
+++ /dev/null
@@ -1,48 +0,0 @@
1/* Header file for utils_disk */
2
3#include "mountlist.h"
4#include "utils_base.h"
5#include "regex.h"
6
7struct name_list {
8 char *name;
9 struct name_list *next;
10};
11
12struct regex_list {
13 regex_t regex;
14 struct regex_list *next;
15};
16
17struct parameter_list {
18 char *name;
19 thresholds *freespace_bytes;
20 thresholds *freespace_units;
21 thresholds *freespace_percent;
22 thresholds *usedspace_bytes;
23 thresholds *usedspace_units;
24 thresholds *usedspace_percent;
25 thresholds *usedinodes_percent;
26 thresholds *freeinodes_percent;
27 char *group;
28 struct mount_entry *best_match;
29 struct parameter_list *name_next;
30 struct parameter_list *name_prev;
31 uintmax_t total, available, available_to_root, used, inodes_free, inodes_free_to_root, inodes_used, inodes_total;
32 double dfree_pct, dused_pct;
33 uint64_t dused_units, dfree_units, dtotal_units;
34 double dused_inodes_percent, dfree_inodes_percent;
35};
36
37void np_add_name(struct name_list **list, const char *name);
38bool np_find_name(struct name_list *list, const char *name);
39bool np_seen_name(struct name_list *list, const char *name);
40int np_add_regex(struct regex_list **list, const char *regex, int cflags);
41bool np_find_regmatch(struct regex_list *list, const char *name);
42struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name);
43struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name);
44struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev);
45
46int search_parameter_list(struct parameter_list *list, const char *name);
47void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact);
48bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re);
diff --git a/lib/utils_tcp.c b/lib/utils_tcp.c
index daae1d54..a82d5a3f 100644
--- a/lib/utils_tcp.c
+++ b/lib/utils_tcp.c
@@ -26,28 +26,35 @@
26 * 26 *
27 *****************************************************************************/ 27 *****************************************************************************/
28 28
29#include "common.h" 29#include "../config.h"
30#include "utils_tcp.h" 30#include "utils_tcp.h"
31#include <stdio.h>
32#include <string.h>
31 33
32#define VERBOSE(message) \ 34#define VERBOSE(message) \
33 do { \ 35 do { \
34 if (flags & NP_MATCH_VERBOSE) \ 36 if (flags & NP_MATCH_VERBOSE) \
35 puts(message); \ 37 puts(message); \
36 } while (0) 38 } while (0)
37 39
38enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count, int flags) { 40enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count,
39 int i, match = 0, partial = 0; 41 int flags) {
40 42 int match = 0;
41 for (i = 0; i < expect_count; i++) { 43 int partial = 0;
42 if (flags & NP_MATCH_VERBOSE) 44 for (int i = 0; i < expect_count; i++) {
43 printf("looking for [%s] %s [%s]\n", server_expect[i], (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status); 45 if (flags & NP_MATCH_VERBOSE) {
46 printf("looking for [%s] %s [%s]\n", server_expect[i],
47 (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status);
48 }
44 49
45 if (flags & NP_MATCH_EXACT) { 50 if (flags & NP_MATCH_EXACT) {
46 if (strncmp(status, server_expect[i], strlen(server_expect[i])) == 0) { 51 if (strncmp(status, server_expect[i], strlen(server_expect[i])) == 0) {
47 VERBOSE("found it"); 52 VERBOSE("found it");
48 match++; 53 match++;
49 continue; 54 continue;
50 } else if (strncmp(status, server_expect[i], strlen(status)) == 0) { 55 }
56
57 if (strncmp(status, server_expect[i], strlen(status)) == 0) {
51 VERBOSE("found a substring"); 58 VERBOSE("found a substring");
52 partial++; 59 partial++;
53 continue; 60 continue;
@@ -60,10 +67,12 @@ enum np_match_result np_expect_match(char *status, char **server_expect, int exp
60 VERBOSE("couldn't find it"); 67 VERBOSE("couldn't find it");
61 } 68 }
62 69
63 if ((flags & NP_MATCH_ALL && match == expect_count) || (!(flags & NP_MATCH_ALL) && match >= 1)) 70 if ((flags & NP_MATCH_ALL && match == expect_count) ||
71 (!(flags & NP_MATCH_ALL) && match >= 1)) {
64 return NP_MATCH_SUCCESS; 72 return NP_MATCH_SUCCESS;
65 else if (partial > 0 || !(flags & NP_MATCH_EXACT)) 73 }
74 if (partial > 0 || !(flags & NP_MATCH_EXACT)) {
66 return NP_MATCH_RETRY; 75 return NP_MATCH_RETRY;
67 else 76 }
68 return NP_MATCH_FAILURE; 77 return NP_MATCH_FAILURE;
69} 78}
diff --git a/lib/utils_tcp.h b/lib/utils_tcp.h
index d5999e9b..e5cdbb82 100644
--- a/lib/utils_tcp.h
+++ b/lib/utils_tcp.h
@@ -11,9 +11,11 @@
11 * server. 11 * server.
12 */ 12 */
13enum np_match_result { 13enum np_match_result {
14 NP_MATCH_NONE,
14 NP_MATCH_FAILURE, 15 NP_MATCH_FAILURE,
15 NP_MATCH_SUCCESS, 16 NP_MATCH_SUCCESS,
16 NP_MATCH_RETRY 17 NP_MATCH_RETRY
17}; 18};
18 19
19enum np_match_result np_expect_match(char *status, char **server_expect, int server_expect_count, int flags); 20enum np_match_result np_expect_match(char *status, char **server_expect, int server_expect_count,
21 int flags);
diff --git a/lib/vendor/cJSON/cJSON.c b/lib/vendor/cJSON/cJSON.c
new file mode 100644
index 00000000..12076e92
--- /dev/null
+++ b/lib/vendor/cJSON/cJSON.c
@@ -0,0 +1,3165 @@
1/*
2 Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21*/
22
23/* cJSON */
24/* JSON parser in C. */
25
26/* disable warnings about old C89 functions in MSVC */
27#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
28#define _CRT_SECURE_NO_DEPRECATE
29#endif
30
31#ifdef __GNUC__
32#pragma GCC visibility push(default)
33#endif
34#if defined(_MSC_VER)
35#pragma warning (push)
36/* disable warning about single line comments in system headers */
37#pragma warning (disable : 4001)
38#endif
39
40#include "../../../config.h"
41#include <string.h>
42#include <stdio.h>
43#include <math.h>
44#include <stdlib.h>
45#include <limits.h>
46#include <ctype.h>
47#include <float.h>
48
49#ifdef ENABLE_LOCALES
50#include <locale.h>
51#endif
52
53#if defined(_MSC_VER)
54#pragma warning (pop)
55#endif
56#ifdef __GNUC__
57#pragma GCC visibility pop
58#endif
59
60#include "cJSON.h"
61
62/* define our own boolean type */
63#ifdef true
64#undef true
65#endif
66#define true ((cJSON_bool)1)
67
68#ifdef false
69#undef false
70#endif
71#define false ((cJSON_bool)0)
72
73/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
74#ifndef isinf
75#define isinf(d) (isnan((d - d)) && !isnan(d))
76#endif
77#ifndef isnan
78#define isnan(d) (d != d)
79#endif
80
81#ifndef NAN
82#ifdef _WIN32
83#define NAN sqrt(-1.0)
84#else
85#define NAN 0.0/0.0
86#endif
87#endif
88
89typedef struct {
90 const unsigned char *json;
91 size_t position;
92} error;
93static error global_error = { NULL, 0 };
94
95CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
96{
97 return (const char*) (global_error.json + global_error.position);
98}
99
100CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
101{
102 if (!cJSON_IsString(item))
103 {
104 return NULL;
105 }
106
107 return item->valuestring;
108}
109
110CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
111{
112 if (!cJSON_IsNumber(item))
113 {
114 return (double) NAN;
115 }
116
117 return item->valuedouble;
118}
119
120/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
121#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
122 #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
123#endif
124
125CJSON_PUBLIC(const char*) cJSON_Version(void)
126{
127 static char version[15];
128 sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
129
130 return version;
131}
132
133/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
134static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
135{
136 if ((string1 == NULL) || (string2 == NULL))
137 {
138 return 1;
139 }
140
141 if (string1 == string2)
142 {
143 return 0;
144 }
145
146 for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
147 {
148 if (*string1 == '\0')
149 {
150 return 0;
151 }
152 }
153
154 return tolower(*string1) - tolower(*string2);
155}
156
157typedef struct internal_hooks
158{
159 void *(CJSON_CDECL *allocate)(size_t size);
160 void (CJSON_CDECL *deallocate)(void *pointer);
161 void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
162} internal_hooks;
163
164#if defined(_MSC_VER)
165/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
166static void * CJSON_CDECL internal_malloc(size_t size)
167{
168 return malloc(size);
169}
170static void CJSON_CDECL internal_free(void *pointer)
171{
172 free(pointer);
173}
174static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
175{
176 return realloc(pointer, size);
177}
178#else
179#define internal_malloc malloc
180#define internal_free free
181#define internal_realloc realloc
182#endif
183
184/* strlen of character literals resolved at compile time */
185#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
186
187static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
188
189static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
190{
191 size_t length = 0;
192 unsigned char *copy = NULL;
193
194 if (string == NULL)
195 {
196 return NULL;
197 }
198
199 length = strlen((const char*)string) + sizeof("");
200 copy = (unsigned char*)hooks->allocate(length);
201 if (copy == NULL)
202 {
203 return NULL;
204 }
205 memcpy(copy, string, length);
206
207 return copy;
208}
209
210CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
211{
212 if (hooks == NULL)
213 {
214 /* Reset hooks */
215 global_hooks.allocate = malloc;
216 global_hooks.deallocate = free;
217 global_hooks.reallocate = realloc;
218 return;
219 }
220
221 global_hooks.allocate = malloc;
222 if (hooks->malloc_fn != NULL)
223 {
224 global_hooks.allocate = hooks->malloc_fn;
225 }
226
227 global_hooks.deallocate = free;
228 if (hooks->free_fn != NULL)
229 {
230 global_hooks.deallocate = hooks->free_fn;
231 }
232
233 /* use realloc only if both free and malloc are used */
234 global_hooks.reallocate = NULL;
235 if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
236 {
237 global_hooks.reallocate = realloc;
238 }
239}
240
241/* Internal constructor. */
242static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
243{
244 cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
245 if (node)
246 {
247 memset(node, '\0', sizeof(cJSON));
248 }
249
250 return node;
251}
252
253/* Delete a cJSON structure. */
254CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
255{
256 cJSON *next = NULL;
257 while (item != NULL)
258 {
259 next = item->next;
260 if (!(item->type & cJSON_IsReference) && (item->child != NULL))
261 {
262 cJSON_Delete(item->child);
263 }
264 if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
265 {
266 global_hooks.deallocate(item->valuestring);
267 item->valuestring = NULL;
268 }
269 if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
270 {
271 global_hooks.deallocate(item->string);
272 item->string = NULL;
273 }
274 global_hooks.deallocate(item);
275 item = next;
276 }
277}
278
279/* get the decimal point character of the current locale */
280static unsigned char get_decimal_point(void)
281{
282#ifdef ENABLE_LOCALES
283 struct lconv *lconv = localeconv();
284 return (unsigned char) lconv->decimal_point[0];
285#else
286 return '.';
287#endif
288}
289
290typedef struct
291{
292 const unsigned char *content;
293 size_t length;
294 size_t offset;
295 size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
296 internal_hooks hooks;
297} parse_buffer;
298
299/* check if the given size is left to read in a given parse buffer (starting with 1) */
300#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
301/* check if the buffer can be accessed at the given index (starting with 0) */
302#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
303#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
304/* get a pointer to the buffer at the position */
305#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
306
307/* Parse the input text to generate a number, and populate the result into item. */
308static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
309{
310 double number = 0;
311 unsigned char *after_end = NULL;
312 unsigned char number_c_string[64];
313 unsigned char decimal_point = get_decimal_point();
314 size_t i = 0;
315
316 if ((input_buffer == NULL) || (input_buffer->content == NULL))
317 {
318 return false;
319 }
320
321 /* copy the number into a temporary buffer and replace '.' with the decimal point
322 * of the current locale (for strtod)
323 * This also takes care of '\0' not necessarily being available for marking the end of the input */
324 for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
325 {
326 switch (buffer_at_offset(input_buffer)[i])
327 {
328 case '0':
329 case '1':
330 case '2':
331 case '3':
332 case '4':
333 case '5':
334 case '6':
335 case '7':
336 case '8':
337 case '9':
338 case '+':
339 case '-':
340 case 'e':
341 case 'E':
342 number_c_string[i] = buffer_at_offset(input_buffer)[i];
343 break;
344
345 case '.':
346 number_c_string[i] = decimal_point;
347 break;
348
349 default:
350 goto loop_end;
351 }
352 }
353loop_end:
354 number_c_string[i] = '\0';
355
356 number = strtod((const char*)number_c_string, (char**)&after_end);
357 if (number_c_string == after_end)
358 {
359 return false; /* parse_error */
360 }
361
362 item->valuedouble = number;
363
364 /* use saturation in case of overflow */
365 if (number >= INT_MAX)
366 {
367 item->valueint = INT_MAX;
368 }
369 else if (number <= (double)INT_MIN)
370 {
371 item->valueint = INT_MIN;
372 }
373 else
374 {
375 item->valueint = (int)number;
376 }
377
378 item->type = cJSON_Number;
379
380 input_buffer->offset += (size_t)(after_end - number_c_string);
381 return true;
382}
383
384/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
385CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
386{
387 if (number >= INT_MAX)
388 {
389 object->valueint = INT_MAX;
390 }
391 else if (number <= (double)INT_MIN)
392 {
393 object->valueint = INT_MIN;
394 }
395 else
396 {
397 object->valueint = (int)number;
398 }
399
400 return object->valuedouble = number;
401}
402
403/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */
404CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
405{
406 char *copy = NULL;
407 size_t v1_len;
408 size_t v2_len;
409 /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
410 if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
411 {
412 return NULL;
413 }
414 /* return NULL if the object is corrupted or valuestring is NULL */
415 if (object->valuestring == NULL || valuestring == NULL)
416 {
417 return NULL;
418 }
419
420 v1_len = strlen(valuestring);
421 v2_len = strlen(object->valuestring);
422
423 if (v1_len <= v2_len)
424 {
425 /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */
426 if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring ))
427 {
428 return NULL;
429 }
430 strcpy(object->valuestring, valuestring);
431 return object->valuestring;
432 }
433 copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
434 if (copy == NULL)
435 {
436 return NULL;
437 }
438 if (object->valuestring != NULL)
439 {
440 cJSON_free(object->valuestring);
441 }
442 object->valuestring = copy;
443
444 return copy;
445}
446
447typedef struct
448{
449 unsigned char *buffer;
450 size_t length;
451 size_t offset;
452 size_t depth; /* current nesting depth (for formatted printing) */
453 cJSON_bool noalloc;
454 cJSON_bool format; /* is this print a formatted print */
455 internal_hooks hooks;
456} printbuffer;
457
458/* realloc printbuffer if necessary to have at least "needed" bytes more */
459static unsigned char* ensure(printbuffer * const p, size_t needed)
460{
461 unsigned char *newbuffer = NULL;
462 size_t newsize = 0;
463
464 if ((p == NULL) || (p->buffer == NULL))
465 {
466 return NULL;
467 }
468
469 if ((p->length > 0) && (p->offset >= p->length))
470 {
471 /* make sure that offset is valid */
472 return NULL;
473 }
474
475 if (needed > INT_MAX)
476 {
477 /* sizes bigger than INT_MAX are currently not supported */
478 return NULL;
479 }
480
481 needed += p->offset + 1;
482 if (needed <= p->length)
483 {
484 return p->buffer + p->offset;
485 }
486
487 if (p->noalloc) {
488 return NULL;
489 }
490
491 /* calculate new buffer size */
492 if (needed > (INT_MAX / 2))
493 {
494 /* overflow of int, use INT_MAX if possible */
495 if (needed <= INT_MAX)
496 {
497 newsize = INT_MAX;
498 }
499 else
500 {
501 return NULL;
502 }
503 }
504 else
505 {
506 newsize = needed * 2;
507 }
508
509 if (p->hooks.reallocate != NULL)
510 {
511 /* reallocate with realloc if available */
512 newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
513 if (newbuffer == NULL)
514 {
515 p->hooks.deallocate(p->buffer);
516 p->length = 0;
517 p->buffer = NULL;
518
519 return NULL;
520 }
521 }
522 else
523 {
524 /* otherwise reallocate manually */
525 newbuffer = (unsigned char*)p->hooks.allocate(newsize);
526 if (!newbuffer)
527 {
528 p->hooks.deallocate(p->buffer);
529 p->length = 0;
530 p->buffer = NULL;
531
532 return NULL;
533 }
534
535 memcpy(newbuffer, p->buffer, p->offset + 1);
536 p->hooks.deallocate(p->buffer);
537 }
538 p->length = newsize;
539 p->buffer = newbuffer;
540
541 return newbuffer + p->offset;
542}
543
544/* calculate the new length of the string in a printbuffer and update the offset */
545static void update_offset(printbuffer * const buffer)
546{
547 const unsigned char *buffer_pointer = NULL;
548 if ((buffer == NULL) || (buffer->buffer == NULL))
549 {
550 return;
551 }
552 buffer_pointer = buffer->buffer + buffer->offset;
553
554 buffer->offset += strlen((const char*)buffer_pointer);
555}
556
557/* securely comparison of floating-point variables */
558static cJSON_bool compare_double(double a, double b)
559{
560 double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
561 return (fabs(a - b) <= maxVal * DBL_EPSILON);
562}
563
564/* Render the number nicely from the given item into a string. */
565static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
566{
567 unsigned char *output_pointer = NULL;
568 double d = item->valuedouble;
569 int length = 0;
570 size_t i = 0;
571 unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
572 unsigned char decimal_point = get_decimal_point();
573 double test = 0.0;
574
575 if (output_buffer == NULL)
576 {
577 return false;
578 }
579
580 /* This checks for NaN and Infinity */
581 if (isnan(d) || isinf(d))
582 {
583 length = sprintf((char*)number_buffer, "null");
584 }
585 else if(d == (double)item->valueint)
586 {
587 length = sprintf((char*)number_buffer, "%d", item->valueint);
588 }
589 else
590 {
591 /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
592 length = sprintf((char*)number_buffer, "%1.15g", d);
593
594 /* Check whether the original double can be recovered */
595 if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
596 {
597 /* If not, print with 17 decimal places of precision */
598 length = sprintf((char*)number_buffer, "%1.17g", d);
599 }
600 }
601
602 /* sprintf failed or buffer overrun occurred */
603 if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
604 {
605 return false;
606 }
607
608 /* reserve appropriate space in the output */
609 output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
610 if (output_pointer == NULL)
611 {
612 return false;
613 }
614
615 /* copy the printed number to the output and replace locale
616 * dependent decimal point with '.' */
617 for (i = 0; i < ((size_t)length); i++)
618 {
619 if (number_buffer[i] == decimal_point)
620 {
621 output_pointer[i] = '.';
622 continue;
623 }
624
625 output_pointer[i] = number_buffer[i];
626 }
627 output_pointer[i] = '\0';
628
629 output_buffer->offset += (size_t)length;
630
631 return true;
632}
633
634/* parse 4 digit hexadecimal number */
635static unsigned parse_hex4(const unsigned char * const input)
636{
637 unsigned int h = 0;
638 size_t i = 0;
639
640 for (i = 0; i < 4; i++)
641 {
642 /* parse digit */
643 if ((input[i] >= '0') && (input[i] <= '9'))
644 {
645 h += (unsigned int) input[i] - '0';
646 }
647 else if ((input[i] >= 'A') && (input[i] <= 'F'))
648 {
649 h += (unsigned int) 10 + input[i] - 'A';
650 }
651 else if ((input[i] >= 'a') && (input[i] <= 'f'))
652 {
653 h += (unsigned int) 10 + input[i] - 'a';
654 }
655 else /* invalid */
656 {
657 return 0;
658 }
659
660 if (i < 3)
661 {
662 /* shift left to make place for the next nibble */
663 h = h << 4;
664 }
665 }
666
667 return h;
668}
669
670/* converts a UTF-16 literal to UTF-8
671 * A literal can be one or two sequences of the form \uXXXX */
672static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
673{
674 long unsigned int codepoint = 0;
675 unsigned int first_code = 0;
676 const unsigned char *first_sequence = input_pointer;
677 unsigned char utf8_length = 0;
678 unsigned char utf8_position = 0;
679 unsigned char sequence_length = 0;
680 unsigned char first_byte_mark = 0;
681
682 if ((input_end - first_sequence) < 6)
683 {
684 /* input ends unexpectedly */
685 goto fail;
686 }
687
688 /* get the first utf16 sequence */
689 first_code = parse_hex4(first_sequence + 2);
690
691 /* check that the code is valid */
692 if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
693 {
694 goto fail;
695 }
696
697 /* UTF16 surrogate pair */
698 if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
699 {
700 const unsigned char *second_sequence = first_sequence + 6;
701 unsigned int second_code = 0;
702 sequence_length = 12; /* \uXXXX\uXXXX */
703
704 if ((input_end - second_sequence) < 6)
705 {
706 /* input ends unexpectedly */
707 goto fail;
708 }
709
710 if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
711 {
712 /* missing second half of the surrogate pair */
713 goto fail;
714 }
715
716 /* get the second utf16 sequence */
717 second_code = parse_hex4(second_sequence + 2);
718 /* check that the code is valid */
719 if ((second_code < 0xDC00) || (second_code > 0xDFFF))
720 {
721 /* invalid second half of the surrogate pair */
722 goto fail;
723 }
724
725
726 /* calculate the unicode codepoint from the surrogate pair */
727 codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
728 }
729 else
730 {
731 sequence_length = 6; /* \uXXXX */
732 codepoint = first_code;
733 }
734
735 /* encode as UTF-8
736 * takes at maximum 4 bytes to encode:
737 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
738 if (codepoint < 0x80)
739 {
740 /* normal ascii, encoding 0xxxxxxx */
741 utf8_length = 1;
742 }
743 else if (codepoint < 0x800)
744 {
745 /* two bytes, encoding 110xxxxx 10xxxxxx */
746 utf8_length = 2;
747 first_byte_mark = 0xC0; /* 11000000 */
748 }
749 else if (codepoint < 0x10000)
750 {
751 /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
752 utf8_length = 3;
753 first_byte_mark = 0xE0; /* 11100000 */
754 }
755 else if (codepoint <= 0x10FFFF)
756 {
757 /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
758 utf8_length = 4;
759 first_byte_mark = 0xF0; /* 11110000 */
760 }
761 else
762 {
763 /* invalid unicode codepoint */
764 goto fail;
765 }
766
767 /* encode as utf8 */
768 for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
769 {
770 /* 10xxxxxx */
771 (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
772 codepoint >>= 6;
773 }
774 /* encode first byte */
775 if (utf8_length > 1)
776 {
777 (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
778 }
779 else
780 {
781 (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
782 }
783
784 *output_pointer += utf8_length;
785
786 return sequence_length;
787
788fail:
789 return 0;
790}
791
792/* Parse the input text into an unescaped cinput, and populate item. */
793static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
794{
795 const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
796 const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
797 unsigned char *output_pointer = NULL;
798 unsigned char *output = NULL;
799
800 /* not a string */
801 if (buffer_at_offset(input_buffer)[0] != '\"')
802 {
803 goto fail;
804 }
805
806 {
807 /* calculate approximate size of the output (overestimate) */
808 size_t allocation_length = 0;
809 size_t skipped_bytes = 0;
810 while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
811 {
812 /* is escape sequence */
813 if (input_end[0] == '\\')
814 {
815 if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
816 {
817 /* prevent buffer overflow when last input character is a backslash */
818 goto fail;
819 }
820 skipped_bytes++;
821 input_end++;
822 }
823 input_end++;
824 }
825 if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
826 {
827 goto fail; /* string ended unexpectedly */
828 }
829
830 /* This is at most how much we need for the output */
831 allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
832 output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
833 if (output == NULL)
834 {
835 goto fail; /* allocation failure */
836 }
837 }
838
839 output_pointer = output;
840 /* loop through the string literal */
841 while (input_pointer < input_end)
842 {
843 if (*input_pointer != '\\')
844 {
845 *output_pointer++ = *input_pointer++;
846 }
847 /* escape sequence */
848 else
849 {
850 unsigned char sequence_length = 2;
851 if ((input_end - input_pointer) < 1)
852 {
853 goto fail;
854 }
855
856 switch (input_pointer[1])
857 {
858 case 'b':
859 *output_pointer++ = '\b';
860 break;
861 case 'f':
862 *output_pointer++ = '\f';
863 break;
864 case 'n':
865 *output_pointer++ = '\n';
866 break;
867 case 'r':
868 *output_pointer++ = '\r';
869 break;
870 case 't':
871 *output_pointer++ = '\t';
872 break;
873 case '\"':
874 case '\\':
875 case '/':
876 *output_pointer++ = input_pointer[1];
877 break;
878
879 /* UTF-16 literal */
880 case 'u':
881 sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
882 if (sequence_length == 0)
883 {
884 /* failed to convert UTF16-literal to UTF-8 */
885 goto fail;
886 }
887 break;
888
889 default:
890 goto fail;
891 }
892 input_pointer += sequence_length;
893 }
894 }
895
896 /* zero terminate the output */
897 *output_pointer = '\0';
898
899 item->type = cJSON_String;
900 item->valuestring = (char*)output;
901
902 input_buffer->offset = (size_t) (input_end - input_buffer->content);
903 input_buffer->offset++;
904
905 return true;
906
907fail:
908 if (output != NULL)
909 {
910 input_buffer->hooks.deallocate(output);
911 output = NULL;
912 }
913
914 if (input_pointer != NULL)
915 {
916 input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
917 }
918
919 return false;
920}
921
922/* Render the cstring provided to an escaped version that can be printed. */
923static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
924{
925 const unsigned char *input_pointer = NULL;
926 unsigned char *output = NULL;
927 unsigned char *output_pointer = NULL;
928 size_t output_length = 0;
929 /* numbers of additional characters needed for escaping */
930 size_t escape_characters = 0;
931
932 if (output_buffer == NULL)
933 {
934 return false;
935 }
936
937 /* empty string */
938 if (input == NULL)
939 {
940 output = ensure(output_buffer, sizeof("\"\""));
941 if (output == NULL)
942 {
943 return false;
944 }
945 strcpy((char*)output, "\"\"");
946
947 return true;
948 }
949
950 /* set "flag" to 1 if something needs to be escaped */
951 for (input_pointer = input; *input_pointer; input_pointer++)
952 {
953 switch (*input_pointer)
954 {
955 case '\"':
956 case '\\':
957 case '\b':
958 case '\f':
959 case '\n':
960 case '\r':
961 case '\t':
962 /* one character escape sequence */
963 escape_characters++;
964 break;
965 default:
966 if (*input_pointer < 32)
967 {
968 /* UTF-16 escape sequence uXXXX */
969 escape_characters += 5;
970 }
971 break;
972 }
973 }
974 output_length = (size_t)(input_pointer - input) + escape_characters;
975
976 output = ensure(output_buffer, output_length + sizeof("\"\""));
977 if (output == NULL)
978 {
979 return false;
980 }
981
982 /* no characters have to be escaped */
983 if (escape_characters == 0)
984 {
985 output[0] = '\"';
986 memcpy(output + 1, input, output_length);
987 output[output_length + 1] = '\"';
988 output[output_length + 2] = '\0';
989
990 return true;
991 }
992
993 output[0] = '\"';
994 output_pointer = output + 1;
995 /* copy the string */
996 for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
997 {
998 if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
999 {
1000 /* normal character, copy */
1001 *output_pointer = *input_pointer;
1002 }
1003 else
1004 {
1005 /* character needs to be escaped */
1006 *output_pointer++ = '\\';
1007 switch (*input_pointer)
1008 {
1009 case '\\':
1010 *output_pointer = '\\';
1011 break;
1012 case '\"':
1013 *output_pointer = '\"';
1014 break;
1015 case '\b':
1016 *output_pointer = 'b';
1017 break;
1018 case '\f':
1019 *output_pointer = 'f';
1020 break;
1021 case '\n':
1022 *output_pointer = 'n';
1023 break;
1024 case '\r':
1025 *output_pointer = 'r';
1026 break;
1027 case '\t':
1028 *output_pointer = 't';
1029 break;
1030 default:
1031 /* escape and print as unicode codepoint */
1032 sprintf((char*)output_pointer, "u%04x", *input_pointer);
1033 output_pointer += 4;
1034 break;
1035 }
1036 }
1037 }
1038 output[output_length + 1] = '\"';
1039 output[output_length + 2] = '\0';
1040
1041 return true;
1042}
1043
1044/* Invoke print_string_ptr (which is useful) on an item. */
1045static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
1046{
1047 return print_string_ptr((unsigned char*)item->valuestring, p);
1048}
1049
1050/* Predeclare these prototypes. */
1051static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
1052static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
1053static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
1054static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
1055static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
1056static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
1057
1058/* Utility to jump whitespace and cr/lf */
1059static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
1060{
1061 if ((buffer == NULL) || (buffer->content == NULL))
1062 {
1063 return NULL;
1064 }
1065
1066 if (cannot_access_at_index(buffer, 0))
1067 {
1068 return buffer;
1069 }
1070
1071 while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
1072 {
1073 buffer->offset++;
1074 }
1075
1076 if (buffer->offset == buffer->length)
1077 {
1078 buffer->offset--;
1079 }
1080
1081 return buffer;
1082}
1083
1084/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
1085static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
1086{
1087 if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
1088 {
1089 return NULL;
1090 }
1091
1092 if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
1093 {
1094 buffer->offset += 3;
1095 }
1096
1097 return buffer;
1098}
1099
1100CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
1101{
1102 size_t buffer_length;
1103
1104 if (NULL == value)
1105 {
1106 return NULL;
1107 }
1108
1109 /* Adding null character size due to require_null_terminated. */
1110 buffer_length = strlen(value) + sizeof("");
1111
1112 return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
1113}
1114
1115/* Parse an object - create a new root, and populate. */
1116CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
1117{
1118 parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
1119 cJSON *item = NULL;
1120
1121 /* reset error position */
1122 global_error.json = NULL;
1123 global_error.position = 0;
1124
1125 if (value == NULL || 0 == buffer_length)
1126 {
1127 goto fail;
1128 }
1129
1130 buffer.content = (const unsigned char*)value;
1131 buffer.length = buffer_length;
1132 buffer.offset = 0;
1133 buffer.hooks = global_hooks;
1134
1135 item = cJSON_New_Item(&global_hooks);
1136 if (item == NULL) /* memory fail */
1137 {
1138 goto fail;
1139 }
1140
1141 if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
1142 {
1143 /* parse failure. ep is set. */
1144 goto fail;
1145 }
1146
1147 /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
1148 if (require_null_terminated)
1149 {
1150 buffer_skip_whitespace(&buffer);
1151 if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
1152 {
1153 goto fail;
1154 }
1155 }
1156 if (return_parse_end)
1157 {
1158 *return_parse_end = (const char*)buffer_at_offset(&buffer);
1159 }
1160
1161 return item;
1162
1163fail:
1164 if (item != NULL)
1165 {
1166 cJSON_Delete(item);
1167 }
1168
1169 if (value != NULL)
1170 {
1171 error local_error;
1172 local_error.json = (const unsigned char*)value;
1173 local_error.position = 0;
1174
1175 if (buffer.offset < buffer.length)
1176 {
1177 local_error.position = buffer.offset;
1178 }
1179 else if (buffer.length > 0)
1180 {
1181 local_error.position = buffer.length - 1;
1182 }
1183
1184 if (return_parse_end != NULL)
1185 {
1186 *return_parse_end = (const char*)local_error.json + local_error.position;
1187 }
1188
1189 global_error = local_error;
1190 }
1191
1192 return NULL;
1193}
1194
1195/* Default options for cJSON_Parse */
1196CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
1197{
1198 return cJSON_ParseWithOpts(value, 0, 0);
1199}
1200
1201CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
1202{
1203 return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
1204}
1205
1206#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
1207
1208static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
1209{
1210 static const size_t default_buffer_size = 256;
1211 printbuffer buffer[1];
1212 unsigned char *printed = NULL;
1213
1214 memset(buffer, 0, sizeof(buffer));
1215
1216 /* create buffer */
1217 buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
1218 buffer->length = default_buffer_size;
1219 buffer->format = format;
1220 buffer->hooks = *hooks;
1221 if (buffer->buffer == NULL)
1222 {
1223 goto fail;
1224 }
1225
1226 /* print the value */
1227 if (!print_value(item, buffer))
1228 {
1229 goto fail;
1230 }
1231 update_offset(buffer);
1232
1233 /* check if reallocate is available */
1234 if (hooks->reallocate != NULL)
1235 {
1236 printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
1237 if (printed == NULL) {
1238 goto fail;
1239 }
1240 buffer->buffer = NULL;
1241 }
1242 else /* otherwise copy the JSON over to a new buffer */
1243 {
1244 printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
1245 if (printed == NULL)
1246 {
1247 goto fail;
1248 }
1249 memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
1250 printed[buffer->offset] = '\0'; /* just to be sure */
1251
1252 /* free the buffer */
1253 hooks->deallocate(buffer->buffer);
1254 buffer->buffer = NULL;
1255 }
1256
1257 return printed;
1258
1259fail:
1260 if (buffer->buffer != NULL)
1261 {
1262 hooks->deallocate(buffer->buffer);
1263 buffer->buffer = NULL;
1264 }
1265
1266 if (printed != NULL)
1267 {
1268 hooks->deallocate(printed);
1269 printed = NULL;
1270 }
1271
1272 return NULL;
1273}
1274
1275/* Render a cJSON item/entity/structure to text. */
1276CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
1277{
1278 return (char*)print(item, true, &global_hooks);
1279}
1280
1281CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
1282{
1283 return (char*)print(item, false, &global_hooks);
1284}
1285
1286CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
1287{
1288 printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1289
1290 if (prebuffer < 0)
1291 {
1292 return NULL;
1293 }
1294
1295 p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
1296 if (!p.buffer)
1297 {
1298 return NULL;
1299 }
1300
1301 p.length = (size_t)prebuffer;
1302 p.offset = 0;
1303 p.noalloc = false;
1304 p.format = fmt;
1305 p.hooks = global_hooks;
1306
1307 if (!print_value(item, &p))
1308 {
1309 global_hooks.deallocate(p.buffer);
1310 p.buffer = NULL;
1311 return NULL;
1312 }
1313
1314 return (char*)p.buffer;
1315}
1316
1317CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
1318{
1319 printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1320
1321 if ((length < 0) || (buffer == NULL))
1322 {
1323 return false;
1324 }
1325
1326 p.buffer = (unsigned char*)buffer;
1327 p.length = (size_t)length;
1328 p.offset = 0;
1329 p.noalloc = true;
1330 p.format = format;
1331 p.hooks = global_hooks;
1332
1333 return print_value(item, &p);
1334}
1335
1336/* Parser core - when encountering text, process appropriately. */
1337static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
1338{
1339 if ((input_buffer == NULL) || (input_buffer->content == NULL))
1340 {
1341 return false; /* no input */
1342 }
1343
1344 /* parse the different types of values */
1345 /* null */
1346 if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
1347 {
1348 item->type = cJSON_NULL;
1349 input_buffer->offset += 4;
1350 return true;
1351 }
1352 /* false */
1353 if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
1354 {
1355 item->type = cJSON_False;
1356 input_buffer->offset += 5;
1357 return true;
1358 }
1359 /* true */
1360 if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
1361 {
1362 item->type = cJSON_True;
1363 item->valueint = 1;
1364 input_buffer->offset += 4;
1365 return true;
1366 }
1367 /* string */
1368 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
1369 {
1370 return parse_string(item, input_buffer);
1371 }
1372 /* number */
1373 if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
1374 {
1375 return parse_number(item, input_buffer);
1376 }
1377 /* array */
1378 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
1379 {
1380 return parse_array(item, input_buffer);
1381 }
1382 /* object */
1383 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
1384 {
1385 return parse_object(item, input_buffer);
1386 }
1387
1388 return false;
1389}
1390
1391/* Render a value to text. */
1392static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
1393{
1394 unsigned char *output = NULL;
1395
1396 if ((item == NULL) || (output_buffer == NULL))
1397 {
1398 return false;
1399 }
1400
1401 switch ((item->type) & 0xFF)
1402 {
1403 case cJSON_NULL:
1404 output = ensure(output_buffer, 5);
1405 if (output == NULL)
1406 {
1407 return false;
1408 }
1409 strcpy((char*)output, "null");
1410 return true;
1411
1412 case cJSON_False:
1413 output = ensure(output_buffer, 6);
1414 if (output == NULL)
1415 {
1416 return false;
1417 }
1418 strcpy((char*)output, "false");
1419 return true;
1420
1421 case cJSON_True:
1422 output = ensure(output_buffer, 5);
1423 if (output == NULL)
1424 {
1425 return false;
1426 }
1427 strcpy((char*)output, "true");
1428 return true;
1429
1430 case cJSON_Number:
1431 return print_number(item, output_buffer);
1432
1433 case cJSON_Raw:
1434 {
1435 size_t raw_length = 0;
1436 if (item->valuestring == NULL)
1437 {
1438 return false;
1439 }
1440
1441 raw_length = strlen(item->valuestring) + sizeof("");
1442 output = ensure(output_buffer, raw_length);
1443 if (output == NULL)
1444 {
1445 return false;
1446 }
1447 memcpy(output, item->valuestring, raw_length);
1448 return true;
1449 }
1450
1451 case cJSON_String:
1452 return print_string(item, output_buffer);
1453
1454 case cJSON_Array:
1455 return print_array(item, output_buffer);
1456
1457 case cJSON_Object:
1458 return print_object(item, output_buffer);
1459
1460 default:
1461 return false;
1462 }
1463}
1464
1465/* Build an array from input text. */
1466static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
1467{
1468 cJSON *head = NULL; /* head of the linked list */
1469 cJSON *current_item = NULL;
1470
1471 if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1472 {
1473 return false; /* to deeply nested */
1474 }
1475 input_buffer->depth++;
1476
1477 if (buffer_at_offset(input_buffer)[0] != '[')
1478 {
1479 /* not an array */
1480 goto fail;
1481 }
1482
1483 input_buffer->offset++;
1484 buffer_skip_whitespace(input_buffer);
1485 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
1486 {
1487 /* empty array */
1488 goto success;
1489 }
1490
1491 /* check if we skipped to the end of the buffer */
1492 if (cannot_access_at_index(input_buffer, 0))
1493 {
1494 input_buffer->offset--;
1495 goto fail;
1496 }
1497
1498 /* step back to character in front of the first element */
1499 input_buffer->offset--;
1500 /* loop through the comma separated array elements */
1501 do
1502 {
1503 /* allocate next item */
1504 cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1505 if (new_item == NULL)
1506 {
1507 goto fail; /* allocation failure */
1508 }
1509
1510 /* attach next item to list */
1511 if (head == NULL)
1512 {
1513 /* start the linked list */
1514 current_item = head = new_item;
1515 }
1516 else
1517 {
1518 /* add to the end and advance */
1519 current_item->next = new_item;
1520 new_item->prev = current_item;
1521 current_item = new_item;
1522 }
1523
1524 /* parse next value */
1525 input_buffer->offset++;
1526 buffer_skip_whitespace(input_buffer);
1527 if (!parse_value(current_item, input_buffer))
1528 {
1529 goto fail; /* failed to parse value */
1530 }
1531 buffer_skip_whitespace(input_buffer);
1532 }
1533 while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1534
1535 if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
1536 {
1537 goto fail; /* expected end of array */
1538 }
1539
1540success:
1541 input_buffer->depth--;
1542
1543 if (head != NULL) {
1544 head->prev = current_item;
1545 }
1546
1547 item->type = cJSON_Array;
1548 item->child = head;
1549
1550 input_buffer->offset++;
1551
1552 return true;
1553
1554fail:
1555 if (head != NULL)
1556 {
1557 cJSON_Delete(head);
1558 }
1559
1560 return false;
1561}
1562
1563/* Render an array to text */
1564static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
1565{
1566 unsigned char *output_pointer = NULL;
1567 size_t length = 0;
1568 cJSON *current_element = item->child;
1569
1570 if (output_buffer == NULL)
1571 {
1572 return false;
1573 }
1574
1575 /* Compose the output array. */
1576 /* opening square bracket */
1577 output_pointer = ensure(output_buffer, 1);
1578 if (output_pointer == NULL)
1579 {
1580 return false;
1581 }
1582
1583 *output_pointer = '[';
1584 output_buffer->offset++;
1585 output_buffer->depth++;
1586
1587 while (current_element != NULL)
1588 {
1589 if (!print_value(current_element, output_buffer))
1590 {
1591 return false;
1592 }
1593 update_offset(output_buffer);
1594 if (current_element->next)
1595 {
1596 length = (size_t) (output_buffer->format ? 2 : 1);
1597 output_pointer = ensure(output_buffer, length + 1);
1598 if (output_pointer == NULL)
1599 {
1600 return false;
1601 }
1602 *output_pointer++ = ',';
1603 if(output_buffer->format)
1604 {
1605 *output_pointer++ = ' ';
1606 }
1607 *output_pointer = '\0';
1608 output_buffer->offset += length;
1609 }
1610 current_element = current_element->next;
1611 }
1612
1613 output_pointer = ensure(output_buffer, 2);
1614 if (output_pointer == NULL)
1615 {
1616 return false;
1617 }
1618 *output_pointer++ = ']';
1619 *output_pointer = '\0';
1620 output_buffer->depth--;
1621
1622 return true;
1623}
1624
1625/* Build an object from the text. */
1626static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
1627{
1628 cJSON *head = NULL; /* linked list head */
1629 cJSON *current_item = NULL;
1630
1631 if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1632 {
1633 return false; /* to deeply nested */
1634 }
1635 input_buffer->depth++;
1636
1637 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
1638 {
1639 goto fail; /* not an object */
1640 }
1641
1642 input_buffer->offset++;
1643 buffer_skip_whitespace(input_buffer);
1644 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
1645 {
1646 goto success; /* empty object */
1647 }
1648
1649 /* check if we skipped to the end of the buffer */
1650 if (cannot_access_at_index(input_buffer, 0))
1651 {
1652 input_buffer->offset--;
1653 goto fail;
1654 }
1655
1656 /* step back to character in front of the first element */
1657 input_buffer->offset--;
1658 /* loop through the comma separated array elements */
1659 do
1660 {
1661 /* allocate next item */
1662 cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1663 if (new_item == NULL)
1664 {
1665 goto fail; /* allocation failure */
1666 }
1667
1668 /* attach next item to list */
1669 if (head == NULL)
1670 {
1671 /* start the linked list */
1672 current_item = head = new_item;
1673 }
1674 else
1675 {
1676 /* add to the end and advance */
1677 current_item->next = new_item;
1678 new_item->prev = current_item;
1679 current_item = new_item;
1680 }
1681
1682 if (cannot_access_at_index(input_buffer, 1))
1683 {
1684 goto fail; /* nothing comes after the comma */
1685 }
1686
1687 /* parse the name of the child */
1688 input_buffer->offset++;
1689 buffer_skip_whitespace(input_buffer);
1690 if (!parse_string(current_item, input_buffer))
1691 {
1692 goto fail; /* failed to parse name */
1693 }
1694 buffer_skip_whitespace(input_buffer);
1695
1696 /* swap valuestring and string, because we parsed the name */
1697 current_item->string = current_item->valuestring;
1698 current_item->valuestring = NULL;
1699
1700 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
1701 {
1702 goto fail; /* invalid object */
1703 }
1704
1705 /* parse the value */
1706 input_buffer->offset++;
1707 buffer_skip_whitespace(input_buffer);
1708 if (!parse_value(current_item, input_buffer))
1709 {
1710 goto fail; /* failed to parse value */
1711 }
1712 buffer_skip_whitespace(input_buffer);
1713 }
1714 while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1715
1716 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
1717 {
1718 goto fail; /* expected end of object */
1719 }
1720
1721success:
1722 input_buffer->depth--;
1723
1724 if (head != NULL) {
1725 head->prev = current_item;
1726 }
1727
1728 item->type = cJSON_Object;
1729 item->child = head;
1730
1731 input_buffer->offset++;
1732 return true;
1733
1734fail:
1735 if (head != NULL)
1736 {
1737 cJSON_Delete(head);
1738 }
1739
1740 return false;
1741}
1742
1743/* Render an object to text. */
1744static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
1745{
1746 unsigned char *output_pointer = NULL;
1747 size_t length = 0;
1748 cJSON *current_item = item->child;
1749
1750 if (output_buffer == NULL)
1751 {
1752 return false;
1753 }
1754
1755 /* Compose the output: */
1756 length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
1757 output_pointer = ensure(output_buffer, length + 1);
1758 if (output_pointer == NULL)
1759 {
1760 return false;
1761 }
1762
1763 *output_pointer++ = '{';
1764 output_buffer->depth++;
1765 if (output_buffer->format)
1766 {
1767 *output_pointer++ = '\n';
1768 }
1769 output_buffer->offset += length;
1770
1771 while (current_item)
1772 {
1773 if (output_buffer->format)
1774 {
1775 size_t i;
1776 output_pointer = ensure(output_buffer, output_buffer->depth);
1777 if (output_pointer == NULL)
1778 {
1779 return false;
1780 }
1781 for (i = 0; i < output_buffer->depth; i++)
1782 {
1783 *output_pointer++ = '\t';
1784 }
1785 output_buffer->offset += output_buffer->depth;
1786 }
1787
1788 /* print key */
1789 if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
1790 {
1791 return false;
1792 }
1793 update_offset(output_buffer);
1794
1795 length = (size_t) (output_buffer->format ? 2 : 1);
1796 output_pointer = ensure(output_buffer, length);
1797 if (output_pointer == NULL)
1798 {
1799 return false;
1800 }
1801 *output_pointer++ = ':';
1802 if (output_buffer->format)
1803 {
1804 *output_pointer++ = '\t';
1805 }
1806 output_buffer->offset += length;
1807
1808 /* print value */
1809 if (!print_value(current_item, output_buffer))
1810 {
1811 return false;
1812 }
1813 update_offset(output_buffer);
1814
1815 /* print comma if not last */
1816 length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
1817 output_pointer = ensure(output_buffer, length + 1);
1818 if (output_pointer == NULL)
1819 {
1820 return false;
1821 }
1822 if (current_item->next)
1823 {
1824 *output_pointer++ = ',';
1825 }
1826
1827 if (output_buffer->format)
1828 {
1829 *output_pointer++ = '\n';
1830 }
1831 *output_pointer = '\0';
1832 output_buffer->offset += length;
1833
1834 current_item = current_item->next;
1835 }
1836
1837 output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
1838 if (output_pointer == NULL)
1839 {
1840 return false;
1841 }
1842 if (output_buffer->format)
1843 {
1844 size_t i;
1845 for (i = 0; i < (output_buffer->depth - 1); i++)
1846 {
1847 *output_pointer++ = '\t';
1848 }
1849 }
1850 *output_pointer++ = '}';
1851 *output_pointer = '\0';
1852 output_buffer->depth--;
1853
1854 return true;
1855}
1856
1857/* Get Array size/item / object item. */
1858CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
1859{
1860 cJSON *child = NULL;
1861 size_t size = 0;
1862
1863 if (array == NULL)
1864 {
1865 return 0;
1866 }
1867
1868 child = array->child;
1869
1870 while(child != NULL)
1871 {
1872 size++;
1873 child = child->next;
1874 }
1875
1876 /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
1877
1878 return (int)size;
1879}
1880
1881static cJSON* get_array_item(const cJSON *array, size_t index)
1882{
1883 cJSON *current_child = NULL;
1884
1885 if (array == NULL)
1886 {
1887 return NULL;
1888 }
1889
1890 current_child = array->child;
1891 while ((current_child != NULL) && (index > 0))
1892 {
1893 index--;
1894 current_child = current_child->next;
1895 }
1896
1897 return current_child;
1898}
1899
1900CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
1901{
1902 if (index < 0)
1903 {
1904 return NULL;
1905 }
1906
1907 return get_array_item(array, (size_t)index);
1908}
1909
1910static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
1911{
1912 cJSON *current_element = NULL;
1913
1914 if ((object == NULL) || (name == NULL))
1915 {
1916 return NULL;
1917 }
1918
1919 current_element = object->child;
1920 if (case_sensitive)
1921 {
1922 while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
1923 {
1924 current_element = current_element->next;
1925 }
1926 }
1927 else
1928 {
1929 while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
1930 {
1931 current_element = current_element->next;
1932 }
1933 }
1934
1935 if ((current_element == NULL) || (current_element->string == NULL)) {
1936 return NULL;
1937 }
1938
1939 return current_element;
1940}
1941
1942CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
1943{
1944 return get_object_item(object, string, false);
1945}
1946
1947CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
1948{
1949 return get_object_item(object, string, true);
1950}
1951
1952CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
1953{
1954 return cJSON_GetObjectItem(object, string) ? 1 : 0;
1955}
1956
1957/* Utility for array list handling. */
1958static void suffix_object(cJSON *prev, cJSON *item)
1959{
1960 prev->next = item;
1961 item->prev = prev;
1962}
1963
1964/* Utility for handling references. */
1965static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
1966{
1967 cJSON *reference = NULL;
1968 if (item == NULL)
1969 {
1970 return NULL;
1971 }
1972
1973 reference = cJSON_New_Item(hooks);
1974 if (reference == NULL)
1975 {
1976 return NULL;
1977 }
1978
1979 memcpy(reference, item, sizeof(cJSON));
1980 reference->string = NULL;
1981 reference->type |= cJSON_IsReference;
1982 reference->next = reference->prev = NULL;
1983 return reference;
1984}
1985
1986static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
1987{
1988 cJSON *child = NULL;
1989
1990 if ((item == NULL) || (array == NULL) || (array == item))
1991 {
1992 return false;
1993 }
1994
1995 child = array->child;
1996 /*
1997 * To find the last item in array quickly, we use prev in array
1998 */
1999 if (child == NULL)
2000 {
2001 /* list is empty, start new one */
2002 array->child = item;
2003 item->prev = item;
2004 item->next = NULL;
2005 }
2006 else
2007 {
2008 /* append to the end */
2009 if (child->prev)
2010 {
2011 suffix_object(child->prev, item);
2012 array->child->prev = item;
2013 }
2014 }
2015
2016 return true;
2017}
2018
2019/* Add item to array/object. */
2020CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
2021{
2022 return add_item_to_array(array, item);
2023}
2024
2025#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2026 #pragma GCC diagnostic push
2027#endif
2028#ifdef __GNUC__
2029#pragma GCC diagnostic ignored "-Wcast-qual"
2030#endif
2031/* helper function to cast away const */
2032static void* cast_away_const(const void* string)
2033{
2034 return (void*)string;
2035}
2036#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2037 #pragma GCC diagnostic pop
2038#endif
2039
2040
2041static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
2042{
2043 char *new_key = NULL;
2044 int new_type = cJSON_Invalid;
2045
2046 if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
2047 {
2048 return false;
2049 }
2050
2051 if (constant_key)
2052 {
2053 new_key = (char*)cast_away_const(string);
2054 new_type = item->type | cJSON_StringIsConst;
2055 }
2056 else
2057 {
2058 new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
2059 if (new_key == NULL)
2060 {
2061 return false;
2062 }
2063
2064 new_type = item->type & ~cJSON_StringIsConst;
2065 }
2066
2067 if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
2068 {
2069 hooks->deallocate(item->string);
2070 }
2071
2072 item->string = new_key;
2073 item->type = new_type;
2074
2075 return add_item_to_array(object, item);
2076}
2077
2078CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
2079{
2080 return add_item_to_object(object, string, item, &global_hooks, false);
2081}
2082
2083/* Add an item to an object with constant string as key */
2084CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
2085{
2086 return add_item_to_object(object, string, item, &global_hooks, true);
2087}
2088
2089CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
2090{
2091 if (array == NULL)
2092 {
2093 return false;
2094 }
2095
2096 return add_item_to_array(array, create_reference(item, &global_hooks));
2097}
2098
2099CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
2100{
2101 if ((object == NULL) || (string == NULL))
2102 {
2103 return false;
2104 }
2105
2106 return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
2107}
2108
2109CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
2110{
2111 cJSON *null = cJSON_CreateNull();
2112 if (add_item_to_object(object, name, null, &global_hooks, false))
2113 {
2114 return null;
2115 }
2116
2117 cJSON_Delete(null);
2118 return NULL;
2119}
2120
2121CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
2122{
2123 cJSON *true_item = cJSON_CreateTrue();
2124 if (add_item_to_object(object, name, true_item, &global_hooks, false))
2125 {
2126 return true_item;
2127 }
2128
2129 cJSON_Delete(true_item);
2130 return NULL;
2131}
2132
2133CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
2134{
2135 cJSON *false_item = cJSON_CreateFalse();
2136 if (add_item_to_object(object, name, false_item, &global_hooks, false))
2137 {
2138 return false_item;
2139 }
2140
2141 cJSON_Delete(false_item);
2142 return NULL;
2143}
2144
2145CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
2146{
2147 cJSON *bool_item = cJSON_CreateBool(boolean);
2148 if (add_item_to_object(object, name, bool_item, &global_hooks, false))
2149 {
2150 return bool_item;
2151 }
2152
2153 cJSON_Delete(bool_item);
2154 return NULL;
2155}
2156
2157CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
2158{
2159 cJSON *number_item = cJSON_CreateNumber(number);
2160 if (add_item_to_object(object, name, number_item, &global_hooks, false))
2161 {
2162 return number_item;
2163 }
2164
2165 cJSON_Delete(number_item);
2166 return NULL;
2167}
2168
2169CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
2170{
2171 cJSON *string_item = cJSON_CreateString(string);
2172 if (add_item_to_object(object, name, string_item, &global_hooks, false))
2173 {
2174 return string_item;
2175 }
2176
2177 cJSON_Delete(string_item);
2178 return NULL;
2179}
2180
2181CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
2182{
2183 cJSON *raw_item = cJSON_CreateRaw(raw);
2184 if (add_item_to_object(object, name, raw_item, &global_hooks, false))
2185 {
2186 return raw_item;
2187 }
2188
2189 cJSON_Delete(raw_item);
2190 return NULL;
2191}
2192
2193CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
2194{
2195 cJSON *object_item = cJSON_CreateObject();
2196 if (add_item_to_object(object, name, object_item, &global_hooks, false))
2197 {
2198 return object_item;
2199 }
2200
2201 cJSON_Delete(object_item);
2202 return NULL;
2203}
2204
2205CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
2206{
2207 cJSON *array = cJSON_CreateArray();
2208 if (add_item_to_object(object, name, array, &global_hooks, false))
2209 {
2210 return array;
2211 }
2212
2213 cJSON_Delete(array);
2214 return NULL;
2215}
2216
2217CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
2218{
2219 if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL))
2220 {
2221 return NULL;
2222 }
2223
2224 if (item != parent->child)
2225 {
2226 /* not the first element */
2227 item->prev->next = item->next;
2228 }
2229 if (item->next != NULL)
2230 {
2231 /* not the last element */
2232 item->next->prev = item->prev;
2233 }
2234
2235 if (item == parent->child)
2236 {
2237 /* first element */
2238 parent->child = item->next;
2239 }
2240 else if (item->next == NULL)
2241 {
2242 /* last element */
2243 parent->child->prev = item->prev;
2244 }
2245
2246 /* make sure the detached item doesn't point anywhere anymore */
2247 item->prev = NULL;
2248 item->next = NULL;
2249
2250 return item;
2251}
2252
2253CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
2254{
2255 if (which < 0)
2256 {
2257 return NULL;
2258 }
2259
2260 return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
2261}
2262
2263CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
2264{
2265 cJSON_Delete(cJSON_DetachItemFromArray(array, which));
2266}
2267
2268CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
2269{
2270 cJSON *to_detach = cJSON_GetObjectItem(object, string);
2271
2272 return cJSON_DetachItemViaPointer(object, to_detach);
2273}
2274
2275CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
2276{
2277 cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
2278
2279 return cJSON_DetachItemViaPointer(object, to_detach);
2280}
2281
2282CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
2283{
2284 cJSON_Delete(cJSON_DetachItemFromObject(object, string));
2285}
2286
2287CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
2288{
2289 cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
2290}
2291
2292/* Replace array/object items with new ones. */
2293CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
2294{
2295 cJSON *after_inserted = NULL;
2296
2297 if (which < 0 || newitem == NULL)
2298 {
2299 return false;
2300 }
2301
2302 after_inserted = get_array_item(array, (size_t)which);
2303 if (after_inserted == NULL)
2304 {
2305 return add_item_to_array(array, newitem);
2306 }
2307
2308 if (after_inserted != array->child && after_inserted->prev == NULL) {
2309 /* return false if after_inserted is a corrupted array item */
2310 return false;
2311 }
2312
2313 newitem->next = after_inserted;
2314 newitem->prev = after_inserted->prev;
2315 after_inserted->prev = newitem;
2316 if (after_inserted == array->child)
2317 {
2318 array->child = newitem;
2319 }
2320 else
2321 {
2322 newitem->prev->next = newitem;
2323 }
2324 return true;
2325}
2326
2327CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
2328{
2329 if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
2330 {
2331 return false;
2332 }
2333
2334 if (replacement == item)
2335 {
2336 return true;
2337 }
2338
2339 replacement->next = item->next;
2340 replacement->prev = item->prev;
2341
2342 if (replacement->next != NULL)
2343 {
2344 replacement->next->prev = replacement;
2345 }
2346 if (parent->child == item)
2347 {
2348 if (parent->child->prev == parent->child)
2349 {
2350 replacement->prev = replacement;
2351 }
2352 parent->child = replacement;
2353 }
2354 else
2355 { /*
2356 * To find the last item in array quickly, we use prev in array.
2357 * We can't modify the last item's next pointer where this item was the parent's child
2358 */
2359 if (replacement->prev != NULL)
2360 {
2361 replacement->prev->next = replacement;
2362 }
2363 if (replacement->next == NULL)
2364 {
2365 parent->child->prev = replacement;
2366 }
2367 }
2368
2369 item->next = NULL;
2370 item->prev = NULL;
2371 cJSON_Delete(item);
2372
2373 return true;
2374}
2375
2376CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
2377{
2378 if (which < 0)
2379 {
2380 return false;
2381 }
2382
2383 return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
2384}
2385
2386static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
2387{
2388 if ((replacement == NULL) || (string == NULL))
2389 {
2390 return false;
2391 }
2392
2393 /* replace the name in the replacement */
2394 if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
2395 {
2396 cJSON_free(replacement->string);
2397 }
2398 replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2399 if (replacement->string == NULL)
2400 {
2401 return false;
2402 }
2403
2404 replacement->type &= ~cJSON_StringIsConst;
2405
2406 return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
2407}
2408
2409CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
2410{
2411 return replace_item_in_object(object, string, newitem, false);
2412}
2413
2414CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
2415{
2416 return replace_item_in_object(object, string, newitem, true);
2417}
2418
2419/* Create basic types: */
2420CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
2421{
2422 cJSON *item = cJSON_New_Item(&global_hooks);
2423 if(item)
2424 {
2425 item->type = cJSON_NULL;
2426 }
2427
2428 return item;
2429}
2430
2431CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
2432{
2433 cJSON *item = cJSON_New_Item(&global_hooks);
2434 if(item)
2435 {
2436 item->type = cJSON_True;
2437 }
2438
2439 return item;
2440}
2441
2442CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
2443{
2444 cJSON *item = cJSON_New_Item(&global_hooks);
2445 if(item)
2446 {
2447 item->type = cJSON_False;
2448 }
2449
2450 return item;
2451}
2452
2453CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
2454{
2455 cJSON *item = cJSON_New_Item(&global_hooks);
2456 if(item)
2457 {
2458 item->type = boolean ? cJSON_True : cJSON_False;
2459 }
2460
2461 return item;
2462}
2463
2464CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
2465{
2466 cJSON *item = cJSON_New_Item(&global_hooks);
2467 if(item)
2468 {
2469 item->type = cJSON_Number;
2470 item->valuedouble = num;
2471
2472 /* use saturation in case of overflow */
2473 if (num >= INT_MAX)
2474 {
2475 item->valueint = INT_MAX;
2476 }
2477 else if (num <= (double)INT_MIN)
2478 {
2479 item->valueint = INT_MIN;
2480 }
2481 else
2482 {
2483 item->valueint = (int)num;
2484 }
2485 }
2486
2487 return item;
2488}
2489
2490CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
2491{
2492 cJSON *item = cJSON_New_Item(&global_hooks);
2493 if(item)
2494 {
2495 item->type = cJSON_String;
2496 item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2497 if(!item->valuestring)
2498 {
2499 cJSON_Delete(item);
2500 return NULL;
2501 }
2502 }
2503
2504 return item;
2505}
2506
2507CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
2508{
2509 cJSON *item = cJSON_New_Item(&global_hooks);
2510 if (item != NULL)
2511 {
2512 item->type = cJSON_String | cJSON_IsReference;
2513 item->valuestring = (char*)cast_away_const(string);
2514 }
2515
2516 return item;
2517}
2518
2519CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
2520{
2521 cJSON *item = cJSON_New_Item(&global_hooks);
2522 if (item != NULL) {
2523 item->type = cJSON_Object | cJSON_IsReference;
2524 item->child = (cJSON*)cast_away_const(child);
2525 }
2526
2527 return item;
2528}
2529
2530CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
2531 cJSON *item = cJSON_New_Item(&global_hooks);
2532 if (item != NULL) {
2533 item->type = cJSON_Array | cJSON_IsReference;
2534 item->child = (cJSON*)cast_away_const(child);
2535 }
2536
2537 return item;
2538}
2539
2540CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
2541{
2542 cJSON *item = cJSON_New_Item(&global_hooks);
2543 if(item)
2544 {
2545 item->type = cJSON_Raw;
2546 item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
2547 if(!item->valuestring)
2548 {
2549 cJSON_Delete(item);
2550 return NULL;
2551 }
2552 }
2553
2554 return item;
2555}
2556
2557CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
2558{
2559 cJSON *item = cJSON_New_Item(&global_hooks);
2560 if(item)
2561 {
2562 item->type=cJSON_Array;
2563 }
2564
2565 return item;
2566}
2567
2568CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
2569{
2570 cJSON *item = cJSON_New_Item(&global_hooks);
2571 if (item)
2572 {
2573 item->type = cJSON_Object;
2574 }
2575
2576 return item;
2577}
2578
2579/* Create Arrays: */
2580CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
2581{
2582 size_t i = 0;
2583 cJSON *n = NULL;
2584 cJSON *p = NULL;
2585 cJSON *a = NULL;
2586
2587 if ((count < 0) || (numbers == NULL))
2588 {
2589 return NULL;
2590 }
2591
2592 a = cJSON_CreateArray();
2593
2594 for(i = 0; a && (i < (size_t)count); i++)
2595 {
2596 n = cJSON_CreateNumber(numbers[i]);
2597 if (!n)
2598 {
2599 cJSON_Delete(a);
2600 return NULL;
2601 }
2602 if(!i)
2603 {
2604 a->child = n;
2605 }
2606 else
2607 {
2608 suffix_object(p, n);
2609 }
2610 p = n;
2611 }
2612
2613 if (a && a->child) {
2614 a->child->prev = n;
2615 }
2616
2617 return a;
2618}
2619
2620CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
2621{
2622 size_t i = 0;
2623 cJSON *n = NULL;
2624 cJSON *p = NULL;
2625 cJSON *a = NULL;
2626
2627 if ((count < 0) || (numbers == NULL))
2628 {
2629 return NULL;
2630 }
2631
2632 a = cJSON_CreateArray();
2633
2634 for(i = 0; a && (i < (size_t)count); i++)
2635 {
2636 n = cJSON_CreateNumber((double)numbers[i]);
2637 if(!n)
2638 {
2639 cJSON_Delete(a);
2640 return NULL;
2641 }
2642 if(!i)
2643 {
2644 a->child = n;
2645 }
2646 else
2647 {
2648 suffix_object(p, n);
2649 }
2650 p = n;
2651 }
2652
2653 if (a && a->child) {
2654 a->child->prev = n;
2655 }
2656
2657 return a;
2658}
2659
2660CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
2661{
2662 size_t i = 0;
2663 cJSON *n = NULL;
2664 cJSON *p = NULL;
2665 cJSON *a = NULL;
2666
2667 if ((count < 0) || (numbers == NULL))
2668 {
2669 return NULL;
2670 }
2671
2672 a = cJSON_CreateArray();
2673
2674 for(i = 0; a && (i < (size_t)count); i++)
2675 {
2676 n = cJSON_CreateNumber(numbers[i]);
2677 if(!n)
2678 {
2679 cJSON_Delete(a);
2680 return NULL;
2681 }
2682 if(!i)
2683 {
2684 a->child = n;
2685 }
2686 else
2687 {
2688 suffix_object(p, n);
2689 }
2690 p = n;
2691 }
2692
2693 if (a && a->child) {
2694 a->child->prev = n;
2695 }
2696
2697 return a;
2698}
2699
2700CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
2701{
2702 size_t i = 0;
2703 cJSON *n = NULL;
2704 cJSON *p = NULL;
2705 cJSON *a = NULL;
2706
2707 if ((count < 0) || (strings == NULL))
2708 {
2709 return NULL;
2710 }
2711
2712 a = cJSON_CreateArray();
2713
2714 for (i = 0; a && (i < (size_t)count); i++)
2715 {
2716 n = cJSON_CreateString(strings[i]);
2717 if(!n)
2718 {
2719 cJSON_Delete(a);
2720 return NULL;
2721 }
2722 if(!i)
2723 {
2724 a->child = n;
2725 }
2726 else
2727 {
2728 suffix_object(p,n);
2729 }
2730 p = n;
2731 }
2732
2733 if (a && a->child) {
2734 a->child->prev = n;
2735 }
2736
2737 return a;
2738}
2739
2740/* Duplication */
2741cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
2742
2743CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
2744{
2745 return cJSON_Duplicate_rec(item, 0, recurse );
2746}
2747
2748cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
2749{
2750 cJSON *newitem = NULL;
2751 cJSON *child = NULL;
2752 cJSON *next = NULL;
2753 cJSON *newchild = NULL;
2754
2755 /* Bail on bad ptr */
2756 if (!item)
2757 {
2758 goto fail;
2759 }
2760 /* Create new item */
2761 newitem = cJSON_New_Item(&global_hooks);
2762 if (!newitem)
2763 {
2764 goto fail;
2765 }
2766 /* Copy over all vars */
2767 newitem->type = item->type & (~cJSON_IsReference);
2768 newitem->valueint = item->valueint;
2769 newitem->valuedouble = item->valuedouble;
2770 if (item->valuestring)
2771 {
2772 newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
2773 if (!newitem->valuestring)
2774 {
2775 goto fail;
2776 }
2777 }
2778 if (item->string)
2779 {
2780 newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
2781 if (!newitem->string)
2782 {
2783 goto fail;
2784 }
2785 }
2786 /* If non-recursive, then we're done! */
2787 if (!recurse)
2788 {
2789 return newitem;
2790 }
2791 /* Walk the ->next chain for the child. */
2792 child = item->child;
2793 while (child != NULL)
2794 {
2795 if(depth >= CJSON_CIRCULAR_LIMIT) {
2796 goto fail;
2797 }
2798 newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */
2799 if (!newchild)
2800 {
2801 goto fail;
2802 }
2803 if (next != NULL)
2804 {
2805 /* If newitem->child already set, then crosswire ->prev and ->next and move on */
2806 next->next = newchild;
2807 newchild->prev = next;
2808 next = newchild;
2809 }
2810 else
2811 {
2812 /* Set newitem->child and move to it */
2813 newitem->child = newchild;
2814 next = newchild;
2815 }
2816 child = child->next;
2817 }
2818 if (newitem && newitem->child)
2819 {
2820 newitem->child->prev = newchild;
2821 }
2822
2823 return newitem;
2824
2825fail:
2826 if (newitem != NULL)
2827 {
2828 cJSON_Delete(newitem);
2829 }
2830
2831 return NULL;
2832}
2833
2834static void skip_oneline_comment(char **input)
2835{
2836 *input += static_strlen("//");
2837
2838 for (; (*input)[0] != '\0'; ++(*input))
2839 {
2840 if ((*input)[0] == '\n') {
2841 *input += static_strlen("\n");
2842 return;
2843 }
2844 }
2845}
2846
2847static void skip_multiline_comment(char **input)
2848{
2849 *input += static_strlen("/*");
2850
2851 for (; (*input)[0] != '\0'; ++(*input))
2852 {
2853 if (((*input)[0] == '*') && ((*input)[1] == '/'))
2854 {
2855 *input += static_strlen("*/");
2856 return;
2857 }
2858 }
2859}
2860
2861static void minify_string(char **input, char **output) {
2862 (*output)[0] = (*input)[0];
2863 *input += static_strlen("\"");
2864 *output += static_strlen("\"");
2865
2866
2867 for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
2868 (*output)[0] = (*input)[0];
2869
2870 if ((*input)[0] == '\"') {
2871 (*output)[0] = '\"';
2872 *input += static_strlen("\"");
2873 *output += static_strlen("\"");
2874 return;
2875 } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
2876 (*output)[1] = (*input)[1];
2877 *input += static_strlen("\"");
2878 *output += static_strlen("\"");
2879 }
2880 }
2881}
2882
2883CJSON_PUBLIC(void) cJSON_Minify(char *json)
2884{
2885 char *into = json;
2886
2887 if (json == NULL)
2888 {
2889 return;
2890 }
2891
2892 while (json[0] != '\0')
2893 {
2894 switch (json[0])
2895 {
2896 case ' ':
2897 case '\t':
2898 case '\r':
2899 case '\n':
2900 json++;
2901 break;
2902
2903 case '/':
2904 if (json[1] == '/')
2905 {
2906 skip_oneline_comment(&json);
2907 }
2908 else if (json[1] == '*')
2909 {
2910 skip_multiline_comment(&json);
2911 } else {
2912 json++;
2913 }
2914 break;
2915
2916 case '\"':
2917 minify_string(&json, (char**)&into);
2918 break;
2919
2920 default:
2921 into[0] = json[0];
2922 json++;
2923 into++;
2924 }
2925 }
2926
2927 /* and null-terminate. */
2928 *into = '\0';
2929}
2930
2931CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
2932{
2933 if (item == NULL)
2934 {
2935 return false;
2936 }
2937
2938 return (item->type & 0xFF) == cJSON_Invalid;
2939}
2940
2941CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
2942{
2943 if (item == NULL)
2944 {
2945 return false;
2946 }
2947
2948 return (item->type & 0xFF) == cJSON_False;
2949}
2950
2951CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
2952{
2953 if (item == NULL)
2954 {
2955 return false;
2956 }
2957
2958 return (item->type & 0xff) == cJSON_True;
2959}
2960
2961
2962CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
2963{
2964 if (item == NULL)
2965 {
2966 return false;
2967 }
2968
2969 return (item->type & (cJSON_True | cJSON_False)) != 0;
2970}
2971CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
2972{
2973 if (item == NULL)
2974 {
2975 return false;
2976 }
2977
2978 return (item->type & 0xFF) == cJSON_NULL;
2979}
2980
2981CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
2982{
2983 if (item == NULL)
2984 {
2985 return false;
2986 }
2987
2988 return (item->type & 0xFF) == cJSON_Number;
2989}
2990
2991CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
2992{
2993 if (item == NULL)
2994 {
2995 return false;
2996 }
2997
2998 return (item->type & 0xFF) == cJSON_String;
2999}
3000
3001CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
3002{
3003 if (item == NULL)
3004 {
3005 return false;
3006 }
3007
3008 return (item->type & 0xFF) == cJSON_Array;
3009}
3010
3011CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
3012{
3013 if (item == NULL)
3014 {
3015 return false;
3016 }
3017
3018 return (item->type & 0xFF) == cJSON_Object;
3019}
3020
3021CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
3022{
3023 if (item == NULL)
3024 {
3025 return false;
3026 }
3027
3028 return (item->type & 0xFF) == cJSON_Raw;
3029}
3030
3031CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
3032{
3033 if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
3034 {
3035 return false;
3036 }
3037
3038 /* check if type is valid */
3039 switch (a->type & 0xFF)
3040 {
3041 case cJSON_False:
3042 case cJSON_True:
3043 case cJSON_NULL:
3044 case cJSON_Number:
3045 case cJSON_String:
3046 case cJSON_Raw:
3047 case cJSON_Array:
3048 case cJSON_Object:
3049 break;
3050
3051 default:
3052 return false;
3053 }
3054
3055 /* identical objects are equal */
3056 if (a == b)
3057 {
3058 return true;
3059 }
3060
3061 switch (a->type & 0xFF)
3062 {
3063 /* in these cases and equal type is enough */
3064 case cJSON_False:
3065 case cJSON_True:
3066 case cJSON_NULL:
3067 return true;
3068
3069 case cJSON_Number:
3070 if (compare_double(a->valuedouble, b->valuedouble))
3071 {
3072 return true;
3073 }
3074 return false;
3075
3076 case cJSON_String:
3077 case cJSON_Raw:
3078 if ((a->valuestring == NULL) || (b->valuestring == NULL))
3079 {
3080 return false;
3081 }
3082 if (strcmp(a->valuestring, b->valuestring) == 0)
3083 {
3084 return true;
3085 }
3086
3087 return false;
3088
3089 case cJSON_Array:
3090 {
3091 cJSON *a_element = a->child;
3092 cJSON *b_element = b->child;
3093
3094 for (; (a_element != NULL) && (b_element != NULL);)
3095 {
3096 if (!cJSON_Compare(a_element, b_element, case_sensitive))
3097 {
3098 return false;
3099 }
3100
3101 a_element = a_element->next;
3102 b_element = b_element->next;
3103 }
3104
3105 /* one of the arrays is longer than the other */
3106 if (a_element != b_element) {
3107 return false;
3108 }
3109
3110 return true;
3111 }
3112
3113 case cJSON_Object:
3114 {
3115 cJSON *a_element = NULL;
3116 cJSON *b_element = NULL;
3117 cJSON_ArrayForEach(a_element, a)
3118 {
3119 /* TODO This has O(n^2) runtime, which is horrible! */
3120 b_element = get_object_item(b, a_element->string, case_sensitive);
3121 if (b_element == NULL)
3122 {
3123 return false;
3124 }
3125
3126 if (!cJSON_Compare(a_element, b_element, case_sensitive))
3127 {
3128 return false;
3129 }
3130 }
3131
3132 /* doing this twice, once on a and b to prevent true comparison if a subset of b
3133 * TODO: Do this the proper way, this is just a fix for now */
3134 cJSON_ArrayForEach(b_element, b)
3135 {
3136 a_element = get_object_item(a, b_element->string, case_sensitive);
3137 if (a_element == NULL)
3138 {
3139 return false;
3140 }
3141
3142 if (!cJSON_Compare(b_element, a_element, case_sensitive))
3143 {
3144 return false;
3145 }
3146 }
3147
3148 return true;
3149 }
3150
3151 default:
3152 return false;
3153 }
3154}
3155
3156CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
3157{
3158 return global_hooks.allocate(size);
3159}
3160
3161CJSON_PUBLIC(void) cJSON_free(void *object)
3162{
3163 global_hooks.deallocate(object);
3164 object = NULL;
3165}
diff --git a/lib/vendor/cJSON/cJSON.h b/lib/vendor/cJSON/cJSON.h
new file mode 100644
index 00000000..37520bbc
--- /dev/null
+++ b/lib/vendor/cJSON/cJSON.h
@@ -0,0 +1,306 @@
1/*
2 Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21*/
22
23#ifndef cJSON__h
24#define cJSON__h
25
26#ifdef __cplusplus
27extern "C"
28{
29#endif
30
31#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
32#define __WINDOWS__
33#endif
34
35#ifdef __WINDOWS__
36
37/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
38
39CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
40CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
41CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
42
43For *nix builds that support visibility attribute, you can define similar behavior by
44
45setting default visibility to hidden by adding
46-fvisibility=hidden (for gcc)
47or
48-xldscope=hidden (for sun cc)
49to CFLAGS
50
51then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
52
53*/
54
55#define CJSON_CDECL __cdecl
56#define CJSON_STDCALL __stdcall
57
58/* export symbols by default, this is necessary for copy pasting the C and header file */
59#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
60#define CJSON_EXPORT_SYMBOLS
61#endif
62
63#if defined(CJSON_HIDE_SYMBOLS)
64#define CJSON_PUBLIC(type) type CJSON_STDCALL
65#elif defined(CJSON_EXPORT_SYMBOLS)
66#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
67#elif defined(CJSON_IMPORT_SYMBOLS)
68#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
69#endif
70#else /* !__WINDOWS__ */
71#define CJSON_CDECL
72#define CJSON_STDCALL
73
74#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
75#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
76#else
77#define CJSON_PUBLIC(type) type
78#endif
79#endif
80
81/* project version */
82#define CJSON_VERSION_MAJOR 1
83#define CJSON_VERSION_MINOR 7
84#define CJSON_VERSION_PATCH 18
85
86#include <stddef.h>
87
88/* cJSON Types: */
89#define cJSON_Invalid (0)
90#define cJSON_False (1 << 0)
91#define cJSON_True (1 << 1)
92#define cJSON_NULL (1 << 2)
93#define cJSON_Number (1 << 3)
94#define cJSON_String (1 << 4)
95#define cJSON_Array (1 << 5)
96#define cJSON_Object (1 << 6)
97#define cJSON_Raw (1 << 7) /* raw json */
98
99#define cJSON_IsReference 256
100#define cJSON_StringIsConst 512
101
102/* The cJSON structure: */
103typedef struct cJSON
104{
105 /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
106 struct cJSON *next;
107 struct cJSON *prev;
108 /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
109 struct cJSON *child;
110
111 /* The type of the item, as above. */
112 int type;
113
114 /* The item's string, if type==cJSON_String and type == cJSON_Raw */
115 char *valuestring;
116 /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
117 int valueint;
118 /* The item's number, if type==cJSON_Number */
119 double valuedouble;
120
121 /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
122 char *string;
123} cJSON;
124
125typedef struct cJSON_Hooks
126{
127 /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
128 void *(CJSON_CDECL *malloc_fn)(size_t sz);
129 void (CJSON_CDECL *free_fn)(void *ptr);
130} cJSON_Hooks;
131
132typedef int cJSON_bool;
133
134/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
135 * This is to prevent stack overflows. */
136#ifndef CJSON_NESTING_LIMIT
137#define CJSON_NESTING_LIMIT 1000
138#endif
139
140/* Limits the length of circular references can be before cJSON rejects to parse them.
141 * This is to prevent stack overflows. */
142#ifndef CJSON_CIRCULAR_LIMIT
143#define CJSON_CIRCULAR_LIMIT 10000
144#endif
145
146/* returns the version of cJSON as a string */
147CJSON_PUBLIC(const char*) cJSON_Version(void);
148
149/* Supply malloc, realloc and free functions to cJSON */
150CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
151
152/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
153/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
154CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
155CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
156/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
157/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
158CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
159CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
160
161/* Render a cJSON entity to text for transfer/storage. */
162CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
163/* Render a cJSON entity to text for transfer/storage without any formatting. */
164CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
165/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
166CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
167/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
168/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
169CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
170/* Delete a cJSON entity and all subentities. */
171CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
172
173/* Returns the number of items in an array (or object). */
174CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
175/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
176CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
177/* Get item "string" from object. Case insensitive. */
178CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
179CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
180CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
181/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
182CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
183
184/* Check item type and return its value */
185CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
186CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
187
188/* These functions check the type of an item */
189CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
190CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
191CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
192CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
193CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
194CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
195CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
196CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
197CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
198CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
199
200/* These calls create a cJSON item of the appropriate type. */
201CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
202CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
203CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
204CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
205CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
206CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
207/* raw json */
208CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
209CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
210CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
211
212/* Create a string where valuestring references a string so
213 * it will not be freed by cJSON_Delete */
214CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
215/* Create an object/array that only references it's elements so
216 * they will not be freed by cJSON_Delete */
217CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
218CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
219
220/* These utilities create an Array of count items.
221 * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
222CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
223CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
224CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
225CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
226
227/* Append item to the specified array/object. */
228CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
229CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
230/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
231 * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
232 * writing to `item->string` */
233CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
234/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
235CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
236CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
237
238/* Remove/Detach items from Arrays/Objects. */
239CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
240CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
241CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
242CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
243CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
244CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
245CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
246
247/* Update array items. */
248CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
249CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
250CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
251CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
252CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
253
254/* Duplicate a cJSON item */
255CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
256/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
257 * need to be released. With recurse!=0, it will duplicate any children connected to the item.
258 * The item->next and ->prev pointers are always zero on return from Duplicate. */
259/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
260 * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
261CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
262
263/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
264 * The input pointer json cannot point to a read-only address area, such as a string constant,
265 * but should point to a readable and writable address area. */
266CJSON_PUBLIC(void) cJSON_Minify(char *json);
267
268/* Helper functions for creating and adding items to an object at the same time.
269 * They return the added item or NULL on failure. */
270CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
271CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
272CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
273CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
274CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
275CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
276CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
277CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
278CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
279
280/* When assigning an integer value, it needs to be propagated to valuedouble too. */
281#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
282/* helper for the cJSON_SetNumberValue macro */
283CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
284#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
285/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
286CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
287
288/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
289#define cJSON_SetBoolValue(object, boolValue) ( \
290 (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
291 (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
292 cJSON_Invalid\
293)
294
295/* Macro for iterating over an array or object */
296#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
297
298/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
299CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
300CJSON_PUBLIC(void) cJSON_free(void *object);
301
302#ifdef __cplusplus
303}
304#endif
305
306#endif
diff --git a/opttest.pl b/opttest.pl
deleted file mode 100755
index 85e3b494..00000000
--- a/opttest.pl
+++ /dev/null
@@ -1,50 +0,0 @@
1#!/usr/bin/perl -w
2use strict;
3use Test;
4
5use vars qw($dir $file $prog $idx $state $output %progs @dirs);
6
7my $tests = 0;
8
9@dirs = qw(plugins plugins-scripts);
10
11foreach $dir (@dirs) {
12 opendir(DIR, $dir) || die "can't opendir $dir: $!";
13 while ($file = readdir(DIR)) {
14 if (-x "$dir/$file" && -f "$dir/$file") {
15 $tests++;
16 $progs{"$dir/$file"} = $file;
17 }
18 }
19 closedir DIR;
20}
21
22plan tests => $tests;
23
24for $prog (keys %progs) {
25 $state = 0;
26 $file = `basename $prog`;
27
28 $idx = 1;
29 $output = `$prog -h 2>&1`;
30 if($?) {$state++;print "$prog failed test $idx\n";}
31 unless ($output =~ m/$progs{$prog}/ms) {
32 $idx++; $state++;print "$output\n$prog failed test $idx\n";
33 }
34
35 $idx++;
36 `$prog --help 2>&1 > /dev/null`;
37 if($?) {$state++;print "$prog failed test $idx\n";}
38
39 $idx++;
40 `$prog -V 2>&1 > /dev/null`;
41 if($?) {$state++;print "$prog failed test $idx\n";}
42
43 $idx++;
44 `$prog --version 2>&1 > /dev/null`;
45 if($?) {$state++;print "$prog failed test $idx\n";}
46
47 print "$prog ($idx tests) ";
48 ok $state,0;
49}
50
diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am
index a80229e2..b6342909 100644
--- a/plugins-root/Makefile.am
+++ b/plugins-root/Makefile.am
@@ -24,7 +24,9 @@ noinst_PROGRAMS = check_dhcp check_icmp @EXTRAS_ROOT@
24 24
25EXTRA_PROGRAMS = pst3 25EXTRA_PROGRAMS = pst3
26 26
27EXTRA_DIST = t pst3.c 27EXTRA_DIST = t pst3.c \
28 check_icmp.d \
29 check_dhcp.d
28 30
29BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a 31BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a
30NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) 32NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS)
@@ -82,6 +84,7 @@ install-exec-local: $(noinst_PROGRAMS)
82# the actual targets 84# the actual targets
83check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) 85check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO)
84check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) 86check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO)
87check_icmp_SOURCES = check_icmp.c check_icmp.d/check_icmp_helpers.c
85 88
86# -m64 needed at compiler and linker phase 89# -m64 needed at compiler and linker phase
87pst3_CFLAGS = @PST3CFLAGS@ 90pst3_CFLAGS = @PST3CFLAGS@
@@ -89,7 +92,7 @@ pst3_LDFLAGS = @PST3CFLAGS@
89# pst3 must not use monitoringplug/gnulib includes! 92# pst3 must not use monitoringplug/gnulib includes!
90pst3_CPPFLAGS = 93pst3_CPPFLAGS =
91 94
92check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) 95check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS)
93check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) 96check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS)
94 97
95clean-local: 98clean-local:
diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c
index 6802232e..9a96547f 100644
--- a/plugins-root/check_dhcp.c
+++ b/plugins-root/check_dhcp.c
@@ -4,7 +4,7 @@
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)
7 * Copyright (c) 2001-2023 Monitoring Plugins Development Team 7 * Copyright (c) 2001-2025 Monitoring Plugins Development Team
8 * 8 *
9 * Description: 9 * Description:
10 * 10 *
@@ -34,13 +34,17 @@
34 *****************************************************************************/ 34 *****************************************************************************/
35 35
36const char *progname = "check_dhcp"; 36const char *progname = "check_dhcp";
37const char *copyright = "2001-2024"; 37const char *copyright = "2001-2025";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "../plugins/common.h"
41#include "netutils.h" 41#include "../plugins/utils.h"
42#include "utils.h" 42#include "./check_dhcp.d/config.h"
43#include "../lib/output.h"
44#include "../lib/utils_base.h"
43 45
46#include "states.h"
47#include <stdint.h>
44#include <ctype.h> 48#include <ctype.h>
45#include <stdio.h> 49#include <stdio.h>
46#include <stdlib.h> 50#include <stdlib.h>
@@ -111,8 +115,9 @@ static long mac_addr_dlpi(const char *, int, u_char *);
111 115
112/**** Common definitions ****/ 116/**** Common definitions ****/
113 117
114#define OK 0 118#define OK 0
115#define ERROR -1 119#define ERROR -1
120#define MAC_ADDR_LEN 6
116 121
117/**** DHCP definitions ****/ 122/**** DHCP definitions ****/
118 123
@@ -122,17 +127,17 @@ static long mac_addr_dlpi(const char *, int, u_char *);
122#define MAX_DHCP_OPTIONS_LENGTH 312 127#define MAX_DHCP_OPTIONS_LENGTH 312
123 128
124typedef struct dhcp_packet_struct { 129typedef struct dhcp_packet_struct {
125 uint8_t op; /* packet type */ 130 uint8_t op; /* packet type */
126 uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ 131 uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */
127 uint8_t hlen; /* length of hardware address (of this machine) */ 132 uint8_t hlen; /* length of hardware address (of this machine) */
128 uint8_t hops; /* hops */ 133 uint8_t hops; /* hops */
129 uint32_t xid; /* random transaction id number - chosen by this machine */ 134 uint32_t xid; /* random transaction id number - chosen by this machine */
130 uint16_t secs; /* seconds used in timing */ 135 uint16_t secs; /* seconds used in timing */
131 uint16_t flags; /* flags */ 136 uint16_t flags; /* flags */
132 struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ 137 struct in_addr ciaddr; /* IP address of this machine (if we already have one) */
133 struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ 138 struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */
134 struct in_addr siaddr; /* IP address of next server */ 139 struct in_addr siaddr; /* IP address of next server */
135 struct in_addr giaddr; /* IP address of DHCP relay */ 140 struct in_addr giaddr; /* IP address of DHCP relay */
136 unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ 141 unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */
137 char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ 142 char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */
138 char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */ 143 char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */
@@ -149,12 +154,6 @@ typedef struct dhcp_offer_struct {
149 struct dhcp_offer_struct *next; 154 struct dhcp_offer_struct *next;
150} dhcp_offer; 155} dhcp_offer;
151 156
152typedef struct requested_server_struct {
153 struct in_addr server_address;
154 bool answered;
155 struct requested_server_struct *next;
156} requested_server;
157
158#define BOOTREQUEST 1 157#define BOOTREQUEST 1
159#define BOOTREPLY 2 158#define BOOTREPLY 2
160 159
@@ -186,65 +185,69 @@ typedef struct requested_server_struct {
186#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ 185#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */
187#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ 186#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */
188 187
189static bool unicast = false; /* unicast mode: mimic a DHCP relay */
190static bool exclusive = false; /* exclusive mode aka "rogue DHCP server detection" */
191static struct in_addr my_ip; /* our address (required for relay) */
192static struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
193static unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
194static unsigned char *user_specified_mac = NULL;
195
196static char network_interface_name[IFNAMSIZ] = "eth0";
197
198static uint32_t packet_xid = 0;
199
200static uint32_t dhcp_lease_time = 0;
201static uint32_t dhcp_renewal_time = 0;
202static uint32_t dhcp_rebinding_time = 0;
203
204static int dhcpoffer_timeout = 2;
205
206static dhcp_offer *dhcp_offer_list = NULL;
207static requested_server *requested_server_list = NULL;
208
209static int valid_responses = 0; /* number of valid DHCPOFFERs we received */
210static int requested_servers = 0;
211static int requested_responses = 0;
212
213static bool request_specific_address = false;
214static bool received_requested_address = false;
215static int verbose = 0; 188static int verbose = 0;
216static struct in_addr requested_address;
217 189
218static int process_arguments(int, char **); 190typedef struct process_arguments_wrapper {
219static int call_getopt(int, char **); 191 int error;
220static int validate_arguments(int); 192 check_dhcp_config config;
193} process_arguments_wrapper;
194
195static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
221void print_usage(void); 196void print_usage(void);
222static void print_help(void); 197static void print_help(void);
223 198
224static void resolve_host(const char *in, struct in_addr *out); 199static void resolve_host(const char * /*in*/, struct in_addr * /*out*/);
225static unsigned char *mac_aton(const char *); 200static unsigned char *mac_aton(const char * /*string*/);
226static void print_hardware_address(const unsigned char *); 201static void print_hardware_address(const unsigned char * /*address*/);
227static int get_hardware_address(int, char *); 202static int get_hardware_address(int /*sock*/, char * /*interface_name*/,
228static int get_ip_address(int, char *); 203 unsigned char *client_hardware_address);
229 204
230static int send_dhcp_discover(int); 205typedef struct get_ip_address_wrapper {
231static int get_dhcp_offer(int); 206 int error;
232 207 struct in_addr my_ip;
233static int get_results(void); 208} get_ip_address_wrapper;
234 209static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/);
235static int add_dhcp_offer(struct in_addr, dhcp_packet *); 210
236static int free_dhcp_offer_list(void); 211typedef struct send_dhcp_discover_wrapper {
237static int free_requested_server_list(void); 212 int error;
238 213 uint32_t packet_xid;
239static int create_dhcp_socket(void); 214} send_dhcp_discover_wrapper;
240static int close_dhcp_socket(int); 215static send_dhcp_discover_wrapper
241static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); 216send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip,
242static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); 217 struct in_addr requested_address, bool request_specific_address,
218 struct in_addr my_ip, unsigned char *client_hardware_address);
219typedef struct get_dhcp_offer_wrapper {
220 int error;
221 int valid_responses;
222 dhcp_offer *dhcp_offer_list;
223} get_dhcp_offer_wrapper;
224static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout,
225 uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
226 const unsigned char *client_hardware_address);
227
228static mp_subcheck get_results(bool exclusive, int requested_servers,
229 struct in_addr requested_address, bool request_specific_address,
230 requested_server *requested_server_list, int valid_responses,
231 dhcp_offer *dhcp_offer_list);
232
233typedef struct add_dhcp_offer_wrapper {
234 int error;
235 dhcp_offer *dhcp_offer_list;
236} add_dhcp_offer_wrapper;
237static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/,
238 dhcp_packet * /*offer_packet*/,
239 dhcp_offer *dhcp_offer_list);
240static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list);
241static int free_requested_server_list(requested_server *requested_server_list);
242
243static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name);
244static int close_dhcp_socket(int /*sock*/);
245static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/,
246 struct sockaddr_in * /*dest*/);
247static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/,
248 int /*timeout*/, struct sockaddr_in * /*address*/);
243 249
244int main(int argc, char **argv) { 250int main(int argc, char **argv) {
245 int dhcp_socket;
246 int result = STATE_UNKNOWN;
247
248 setlocale(LC_ALL, ""); 251 setlocale(LC_ALL, "");
249 bindtextdomain(PACKAGE, LOCALEDIR); 252 bindtextdomain(PACKAGE, LOCALEDIR);
250 textdomain(PACKAGE); 253 textdomain(PACKAGE);
@@ -252,43 +255,84 @@ int main(int argc, char **argv) {
252 /* Parse extra opts if any */ 255 /* Parse extra opts if any */
253 argv = np_extra_opts(&argc, argv, progname); 256 argv = np_extra_opts(&argc, argv, progname);
254 257
255 if (process_arguments(argc, argv) != OK) { 258 process_arguments_wrapper tmp = process_arguments(argc, argv);
259
260 if (tmp.error != OK) {
256 usage4(_("Could not parse arguments")); 261 usage4(_("Could not parse arguments"));
257 } 262 }
258 263
264 check_dhcp_config config = tmp.config;
265 if (config.output_format_is_set) {
266 mp_set_format(config.output_format);
267 }
268
259 /* create socket for DHCP communications */ 269 /* create socket for DHCP communications */
260 dhcp_socket = create_dhcp_socket(); 270 int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name);
261 271
262 /* get hardware address of client machine */ 272 /* get hardware address of client machine */
263 if (user_specified_mac != NULL) 273 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
264 memcpy(client_hardware_address, user_specified_mac, 6); 274 if (config.user_specified_mac != NULL) {
265 else 275 memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN);
266 get_hardware_address(dhcp_socket, network_interface_name); 276 } else {
277 get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address);
278 }
267 279
268 if (unicast) /* get IP address of client machine */ 280 struct in_addr my_ip = {0};
269 get_ip_address(dhcp_socket, network_interface_name); 281
282 if (config.unicast_mode) { /* get IP address of client machine */
283 get_ip_address_wrapper tmp_get_ip =
284 get_ip_address(dhcp_socket, config.network_interface_name);
285 if (tmp_get_ip.error == OK) {
286 my_ip = tmp_get_ip.my_ip;
287 } else {
288 // TODO failed to get own IP
289 die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode");
290 }
291 }
270 292
271 /* send DHCPDISCOVER packet */ 293 /* send DHCPDISCOVER packet */
272 send_dhcp_discover(dhcp_socket); 294 send_dhcp_discover_wrapper disco_res = send_dhcp_discover(
295 dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address,
296 config.request_specific_address, my_ip, client_hardware_address);
297
298 if (disco_res.error != OK) {
299 // DO something?
300 die(STATE_UNKNOWN, "Failed to send DHCP discover");
301 }
273 302
274 /* wait for a DHCPOFFER packet */ 303 /* wait for a DHCPOFFER packet */
275 get_dhcp_offer(dhcp_socket); 304 get_dhcp_offer_wrapper offer_res = get_dhcp_offer(
305 dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address);
306
307 int valid_responses = 0;
308 dhcp_offer *dhcp_offer_list = NULL;
309 if (offer_res.error == OK) {
310 valid_responses = offer_res.valid_responses;
311 dhcp_offer_list = offer_res.dhcp_offer_list;
312 } else {
313 die(STATE_UNKNOWN, "Failed to get DHCP offers");
314 }
276 315
277 /* close socket we created */ 316 /* close socket we created */
278 close_dhcp_socket(dhcp_socket); 317 close_dhcp_socket(dhcp_socket);
279 318
280 /* determine state/plugin output to return */ 319 mp_check overall = mp_check_init();
281 result = get_results();
282 320
321 /* determine state/plugin output to return */
322 mp_subcheck sc_res =
323 get_results(config.exclusive_mode, config.num_of_requested_servers,
324 config.requested_address, config.request_specific_address,
325 config.requested_server_list, valid_responses, dhcp_offer_list);
326 mp_add_subcheck_to_check(&overall, sc_res);
283 /* free allocated memory */ 327 /* free allocated memory */
284 free_dhcp_offer_list(); 328 free_dhcp_offer_list(dhcp_offer_list);
285 free_requested_server_list(); 329 free_requested_server_list(config.requested_server_list);
286 330
287 return result; 331 mp_exit(overall);
288} 332}
289 333
290/* determines hardware address on client machine */ 334/* determines hardware address on client machine */
291static int get_hardware_address(int sock, char *interface_name) { 335int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) {
292 336
293#if defined(__linux__) 337#if defined(__linux__)
294 struct ifreq ifr; 338 struct ifreq ifr;
@@ -302,7 +346,7 @@ static int get_hardware_address(int sock, char *interface_name) {
302 exit(STATE_UNKNOWN); 346 exit(STATE_UNKNOWN);
303 } 347 }
304 348
305 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); 349 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN);
306 350
307#elif defined(__bsd__) 351#elif defined(__bsd__)
308 /* King 2004 see ACKNOWLEDGEMENTS */ 352 /* King 2004 see ACKNOWLEDGEMENTS */
@@ -326,17 +370,20 @@ static int get_hardware_address(int sock, char *interface_name) {
326 } 370 }
327 371
328 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { 372 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
329 printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), interface_name, strerror(errno)); 373 printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"),
374 interface_name, strerror(errno));
330 exit(STATE_UNKNOWN); 375 exit(STATE_UNKNOWN);
331 } 376 }
332 377
333 if ((buf = malloc(len)) == NULL) { 378 if ((buf = malloc(len)) == NULL) {
334 printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), interface_name, strerror(errno)); 379 printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"),
380 interface_name, strerror(errno));
335 exit(4); 381 exit(4);
336 } 382 }
337 383
338 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 384 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
339 printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), interface_name, strerror(errno)); 385 printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"),
386 interface_name, strerror(errno));
340 exit(STATE_UNKNOWN); 387 exit(STATE_UNKNOWN);
341 } 388 }
342 389
@@ -358,20 +405,25 @@ static int get_hardware_address(int sock, char *interface_name) {
358 int i; 405 int i;
359 p = interface_name + strlen(interface_name) - 1; 406 p = interface_name + strlen(interface_name) - 1;
360 for (i = strlen(interface_name) - 1; i > 0; p--) { 407 for (i = strlen(interface_name) - 1; i > 0; p--) {
361 if (isalpha(*p)) 408 if (isalpha(*p)) {
362 break; 409 break;
410 }
363 } 411 }
364 p++; 412 p++;
365 if (p != interface_name) { 413 if (p != interface_name) {
366 unit = atoi(p); 414 unit = atoi(p);
367 strncat(dev, interface_name, 6); 415 strncat(dev, interface_name, 6);
368 } else { 416 } else {
369 printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name); 417 printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg "
418 "lnc0.\n"),
419 interface_name);
370 exit(STATE_UNKNOWN); 420 exit(STATE_UNKNOWN);
371 } 421 }
372 stat = mac_addr_dlpi(dev, unit, client_hardware_address); 422 stat = mac_addr_dlpi(dev, unit, client_hardware_address);
373 if (stat != 0) { 423 if (stat != 0) {
374 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); 424 printf(
425 _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"),
426 dev, unit);
375 exit(STATE_UNKNOWN); 427 exit(STATE_UNKNOWN);
376 } 428 }
377 429
@@ -383,7 +435,9 @@ static int get_hardware_address(int sock, char *interface_name) {
383 435
384 stat = mac_addr_dlpi(dev, unit, client_hardware_address); 436 stat = mac_addr_dlpi(dev, unit, client_hardware_address);
385 if (stat != 0) { 437 if (stat != 0) {
386 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); 438 printf(
439 _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"),
440 dev, unit);
387 exit(STATE_UNKNOWN); 441 exit(STATE_UNKNOWN);
388 } 442 }
389 /* Kompf 2000-2003 */ 443 /* Kompf 2000-2003 */
@@ -393,14 +447,15 @@ static int get_hardware_address(int sock, char *interface_name) {
393 exit(STATE_UNKNOWN); 447 exit(STATE_UNKNOWN);
394#endif 448#endif
395 449
396 if (verbose) 450 if (verbose) {
397 print_hardware_address(client_hardware_address); 451 print_hardware_address(client_hardware_address);
452 }
398 453
399 return OK; 454 return OK;
400} 455}
401 456
402/* determines IP address of the client interface */ 457/* determines IP address of the client interface */
403static int get_ip_address(int sock, char *interface_name) { 458get_ip_address_wrapper get_ip_address(int sock, char *interface_name) {
404#if defined(SIOCGIFADDR) 459#if defined(SIOCGIFADDR)
405 struct ifreq ifr; 460 struct ifreq ifr;
406 461
@@ -412,28 +467,30 @@ static int get_ip_address(int sock, char *interface_name) {
412 exit(STATE_UNKNOWN); 467 exit(STATE_UNKNOWN);
413 } 468 }
414 469
415 my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
416
417#else 470#else
418 printf(_("Error: Cannot get interface IP address on this platform.\n")); 471 printf(_("Error: Cannot get interface IP address on this platform.\n"));
419 exit(STATE_UNKNOWN); 472 exit(STATE_UNKNOWN);
420#endif 473#endif
421 474
422 if (verbose) 475 get_ip_address_wrapper result = {
423 printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); 476 .error = OK,
477 .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr,
478 };
424 479
425 return OK; 480 if (verbose) {
481 printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip));
482 }
483
484 return result;
426} 485}
427 486
428/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ 487/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
429static int send_dhcp_discover(int sock) { 488static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip,
430 dhcp_packet discover_packet; 489 struct in_addr requested_address,
431 struct sockaddr_in sockaddr_broadcast; 490 bool request_specific_address,
432 unsigned short opts; 491 struct in_addr my_ip,
433 492 unsigned char *client_hardware_address) {
434 /* clear the packet data structure */ 493 dhcp_packet discover_packet = {0};
435 bzero(&discover_packet, sizeof(discover_packet));
436
437 /* boot request flag (backward compatible with BOOTP servers) */ 494 /* boot request flag (backward compatible with BOOTP servers) */
438 discover_packet.op = BOOTREQUEST; 495 discover_packet.op = BOOTREQUEST;
439 496
@@ -443,12 +500,15 @@ static int send_dhcp_discover(int sock) {
443 /* length of our hardware address */ 500 /* length of our hardware address */
444 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; 501 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH;
445 502
503 send_dhcp_discover_wrapper result = {
504 .error = OK,
505 };
446 /* 506 /*
447 * transaction ID is supposed to be random. 507 * transaction ID is supposed to be random.
448 */ 508 */
449 srand(time(NULL) ^ getpid()); 509 srand(time(NULL) ^ getpid());
450 packet_xid = random(); 510 result.packet_xid = random();
451 discover_packet.xid = htonl(packet_xid); 511 discover_packet.xid = htonl(result.packet_xid);
452 512
453 /*discover_packet.secs=htons(65535);*/ 513 /*discover_packet.secs=htons(65535);*/
454 discover_packet.secs = 0xFF; 514 discover_packet.secs = 0xFF;
@@ -468,10 +528,11 @@ static int send_dhcp_discover(int sock) {
468 discover_packet.options[2] = '\x53'; 528 discover_packet.options[2] = '\x53';
469 discover_packet.options[3] = '\x63'; 529 discover_packet.options[3] = '\x63';
470 530
471 opts = 4; 531 unsigned short opts = 4;
472 /* DHCP message type is embedded in options field */ 532 /* DHCP message type is embedded in options field */
473 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ 533 discover_packet.options[opts++] =
474 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ 534 DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
535 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */
475 discover_packet.options[opts++] = DHCPDISCOVER; 536 discover_packet.options[opts++] = DHCPDISCOVER;
476 537
477 /* the IP address we're requesting */ 538 /* the IP address we're requesting */
@@ -484,21 +545,25 @@ static int send_dhcp_discover(int sock) {
484 discover_packet.options[opts++] = (char)DHCP_OPTION_END; 545 discover_packet.options[opts++] = (char)DHCP_OPTION_END;
485 546
486 /* unicast fields */ 547 /* unicast fields */
487 if (unicast) 548 if (unicast) {
488 discover_packet.giaddr.s_addr = my_ip.s_addr; 549 discover_packet.giaddr.s_addr = my_ip.s_addr;
550 }
489 551
490 /* see RFC 1542, 4.1.1 */ 552 /* see RFC 1542, 4.1.1 */
491 discover_packet.hops = unicast ? 1 : 0; 553 discover_packet.hops = unicast ? 1 : 0;
492 554
493 /* send the DHCPDISCOVER packet to broadcast address */ 555 /* send the DHCPDISCOVER packet to broadcast address */
494 sockaddr_broadcast.sin_family = AF_INET; 556 struct sockaddr_in sockaddr_broadcast = {
495 sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); 557 .sin_family = AF_INET,
496 sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; 558 .sin_port = htons(DHCP_SERVER_PORT),
497 bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); 559 .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST,
560 };
498 561
499 if (verbose) { 562 if (verbose) {
500 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); 563 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr),
501 printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid), ntohl(discover_packet.xid)); 564 ntohs(sockaddr_broadcast.sin_port));
565 printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid),
566 ntohl(discover_packet.xid));
502 printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr)); 567 printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr));
503 printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr)); 568 printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr));
504 printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr)); 569 printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr));
@@ -508,56 +573,58 @@ static int send_dhcp_discover(int sock) {
508 /* send the DHCPDISCOVER packet out */ 573 /* send the DHCPDISCOVER packet out */
509 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); 574 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast);
510 575
511 if (verbose) 576 if (verbose) {
512 printf("\n\n"); 577 printf("\n\n");
578 }
513 579
514 return OK; 580 return result;
515} 581}
516 582
517/* waits for a DHCPOFFER message from one or more DHCP servers */ 583/* waits for a DHCPOFFER message from one or more DHCP servers */
518static int get_dhcp_offer(int sock) { 584get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid,
519 dhcp_packet offer_packet; 585 dhcp_offer *dhcp_offer_list,
520 struct sockaddr_in source; 586 const unsigned char *client_hardware_address) {
521 struct sockaddr_in via;
522 int result = OK;
523 int responses = 0;
524 int x;
525 time_t start_time; 587 time_t start_time;
526 time_t current_time;
527
528 time(&start_time); 588 time(&start_time);
529 589
590 int result = OK;
591 int responses = 0;
592 int valid_responses = 0;
530 /* receive as many responses as we can */ 593 /* receive as many responses as we can */
531 for (responses = 0, valid_responses = 0;;) { 594 for (;;) {
532 595 time_t current_time;
533 time(&current_time); 596 time(&current_time);
534 if ((current_time - start_time) >= dhcpoffer_timeout) 597 if ((current_time - start_time) >= dhcpoffer_timeout) {
535 break; 598 break;
599 }
536 600
537 if (verbose) 601 if (verbose) {
538 printf("\n\n"); 602 printf("\n\n");
603 }
539 604
540 bzero(&source, sizeof(source)); 605 struct sockaddr_in source = {0};
541 bzero(&via, sizeof(via)); 606 dhcp_packet offer_packet = {0};
542 bzero(&offer_packet, sizeof(offer_packet));
543 607
544 result = OK; 608 result = OK;
545 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); 609 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout,
610 &source);
546 611
547 if (result != OK) { 612 if (result != OK) {
548 if (verbose) 613 if (verbose) {
549 printf(_("Result=ERROR\n")); 614 printf(_("Result=ERROR\n"));
615 }
550 616
551 continue; 617 continue;
552 } else { 618 }
553 if (verbose) 619 if (verbose) {
554 printf(_("Result=OK\n")); 620 printf(_("Result=OK\n"));
555
556 responses++;
557 } 621 }
558 622
623 responses++;
624
559 /* The "source" is either a server or a relay. */ 625 /* The "source" is either a server or a relay. */
560 /* Save a copy of "source" into "via" even if it's via itself */ 626 /* Save a copy of "source" into "via" even if it's via itself */
627 struct sockaddr_in via = {0};
561 memcpy(&via, &source, sizeof(source)); 628 memcpy(&via, &source, sizeof(source));
562 629
563 if (verbose) { 630 if (verbose) {
@@ -568,30 +635,38 @@ static int get_dhcp_offer(int sock) {
568 635
569 /* check packet xid to see if its the same as the one we used in the discover packet */ 636 /* check packet xid to see if its the same as the one we used in the discover packet */
570 if (ntohl(offer_packet.xid) != packet_xid) { 637 if (ntohl(offer_packet.xid) != packet_xid) {
571 if (verbose) 638 if (verbose) {
572 printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), packet_xid); 639 printf(
640 _("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"),
641 ntohl(offer_packet.xid), packet_xid);
642 }
573 643
574 continue; 644 continue;
575 } 645 }
576 646
577 /* check hardware address */ 647 /* check hardware address */
578 result = OK; 648 result = OK;
579 if (verbose) 649 if (verbose) {
580 printf("DHCPOFFER chaddr: "); 650 printf("DHCPOFFER chaddr: ");
651 }
581 652
582 for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { 653 for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) {
583 if (verbose) 654 if (verbose) {
584 printf("%02X", (unsigned char)offer_packet.chaddr[x]); 655 printf("%02X", offer_packet.chaddr[i]);
656 }
585 657
586 if (offer_packet.chaddr[x] != client_hardware_address[x]) 658 if (offer_packet.chaddr[i] != client_hardware_address[i]) {
587 result = ERROR; 659 result = ERROR;
660 }
588 } 661 }
589 if (verbose) 662 if (verbose) {
590 printf("\n"); 663 printf("\n");
664 }
591 665
592 if (result == ERROR) { 666 if (result == ERROR) {
593 if (verbose) 667 if (verbose) {
594 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); 668 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
669 }
595 670
596 continue; 671 continue;
597 } 672 }
@@ -603,7 +678,13 @@ static int get_dhcp_offer(int sock) {
603 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); 678 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr));
604 } 679 }
605 680
606 add_dhcp_offer(source.sin_addr, &offer_packet); 681 add_dhcp_offer_wrapper add_res =
682 add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list);
683 if (add_res.error != OK) {
684 // TODO
685 } else {
686 dhcp_offer_list = add_res.dhcp_offer_list;
687 }
607 688
608 valid_responses++; 689 valid_responses++;
609 } 690 }
@@ -613,104 +694,104 @@ static int get_dhcp_offer(int sock) {
613 printf(_("Valid responses for this machine: %d\n"), valid_responses); 694 printf(_("Valid responses for this machine: %d\n"), valid_responses);
614 } 695 }
615 696
616 return OK; 697 get_dhcp_offer_wrapper ret_val = {
698 .error = OK,
699 .valid_responses = valid_responses,
700 .dhcp_offer_list = dhcp_offer_list,
701 };
702 return ret_val;
617} 703}
618 704
619/* sends a DHCP packet */ 705/* sends a DHCP packet */
620static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { 706int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) {
621 int result; 707 int result =
622 708 sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
623 result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
624 709
625 if (verbose) 710 if (verbose) {
626 printf(_("send_dhcp_packet result: %d\n"), result); 711 printf(_("send_dhcp_packet result: %d\n"), result);
712 }
627 713
628 if (result < 0) 714 if (result < 0) {
629 return ERROR; 715 return ERROR;
716 }
630 717
631 return OK; 718 return OK;
632} 719}
633 720
634/* receives a DHCP packet */ 721/* receives a DHCP packet */
635static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { 722int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout,
636 struct timeval tv; 723 struct sockaddr_in *address) {
637 fd_set readfds;
638 fd_set oobfds;
639 int recv_result;
640 socklen_t address_size;
641 struct sockaddr_in source_address;
642 int nfound;
643
644 /* wait for data to arrive (up time timeout) */ 724 /* wait for data to arrive (up time timeout) */
645 tv.tv_sec = timeout; 725 struct timeval timeout_val = {
646 tv.tv_usec = 0; 726 .tv_sec = timeout,
727 .tv_usec = 0,
728 };
729 fd_set readfds;
647 FD_ZERO(&readfds); 730 FD_ZERO(&readfds);
731 fd_set oobfds;
648 FD_ZERO(&oobfds); 732 FD_ZERO(&oobfds);
649 FD_SET(sock, &readfds); 733 FD_SET(sock, &readfds);
650 FD_SET(sock, &oobfds); 734 FD_SET(sock, &oobfds);
651 nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); 735 int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val);
652 736
653 /* make sure some data has arrived */ 737 /* make sure some data has arrived */
654 if (!FD_ISSET(sock, &readfds)) { 738 if (!FD_ISSET(sock, &readfds)) {
655 if (verbose) 739 if (verbose) {
656 printf(_("No (more) data received (nfound: %d)\n"), nfound); 740 printf(_("No (more) data received (nfound: %d)\n"), nfound);
741 }
657 return ERROR; 742 return ERROR;
658 } 743 }
659 744
660 else { 745 struct sockaddr_in source_address = {0};
661 bzero(&source_address, sizeof(source_address)); 746 socklen_t address_size = sizeof(source_address);
662 address_size = sizeof(source_address); 747 int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0,
663 recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); 748 (struct sockaddr *)&source_address, &address_size);
664 if (verbose) 749 if (verbose) {
665 printf("recv_result: %d\n", recv_result); 750 printf("recv_result: %d\n", recv_result);
666 751 }
667 if (recv_result == -1) {
668 if (verbose) {
669 printf(_("recvfrom() failed, "));
670 printf("errno: (%d) -> %s\n", errno, strerror(errno));
671 }
672 return ERROR;
673 } else {
674 if (verbose) {
675 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
676 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
677 }
678 752
679 memcpy(address, &source_address, sizeof(source_address)); 753 if (recv_result == -1) {
680 return OK; 754 if (verbose) {
755 printf(_("recvfrom() failed, "));
756 printf("errno: (%d) -> %s\n", errno, strerror(errno));
681 } 757 }
758 return ERROR;
759 }
760 if (verbose) {
761 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
762 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
682 } 763 }
683 764
765 memcpy(address, &source_address, sizeof(source_address));
684 return OK; 766 return OK;
685} 767}
686 768
687/* creates a socket for DHCP communication */ 769/* creates a socket for DHCP communication */
688static int create_dhcp_socket(void) { 770int create_dhcp_socket(bool unicast, char *network_interface_name) {
689 struct sockaddr_in myname;
690 struct ifreq interface;
691 int sock;
692 int flag = 1;
693
694 /* Set up the address we're going to bind to. */ 771 /* Set up the address we're going to bind to. */
695 bzero(&myname, sizeof(myname));
696 myname.sin_family = AF_INET;
697 /* listen to DHCP server port if we're in unicast mode */ 772 /* listen to DHCP server port if we're in unicast mode */
698 myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); 773 struct sockaddr_in myname = {
699 myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; 774 .sin_family = AF_INET,
700 bzero(&myname.sin_zero, sizeof(myname.sin_zero)); 775 .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT),
776 // TODO previously the next line was trying to use our own IP, we was not set
777 // until some point later, so it was removed. Recheck whether it is actually
778 // necessary/useful
779 .sin_addr.s_addr = INADDR_ANY,
780 };
701 781
702 /* create a socket for DHCP communications */ 782 /* create a socket for DHCP communications */
703 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 783 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
704 if (sock < 0) { 784 if (sock < 0) {
705 printf(_("Error: Could not create socket!\n")); 785 printf(_("Error: Could not create socket!\n"));
706 exit(STATE_UNKNOWN); 786 exit(STATE_UNKNOWN);
707 } 787 }
708 788
709 if (verbose) 789 if (verbose) {
710 printf("DHCP socket: %d\n", sock); 790 printf("DHCP socket: %d\n", sock);
791 }
711 792
712 /* set the reuse address flag so we don't get errors when restarting */ 793 /* set the reuse address flag so we don't get errors when restarting */
713 flag = 1; 794 int flag = 1;
714 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { 795 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) {
715 printf(_("Error: Could not set reuse address option on DHCP socket!\n")); 796 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
716 exit(STATE_UNKNOWN); 797 exit(STATE_UNKNOWN);
@@ -722,12 +803,14 @@ static int create_dhcp_socket(void) {
722 exit(STATE_UNKNOWN); 803 exit(STATE_UNKNOWN);
723 } 804 }
724 805
806 struct ifreq interface;
725 /* bind socket to interface */ 807 /* bind socket to interface */
726#if defined(__linux__) 808#if defined(__linux__)
727 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); 809 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1);
728 interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; 810 interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0';
729 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { 811 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) {
730 printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"), network_interface_name); 812 printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"),
813 network_interface_name);
731 exit(STATE_UNKNOWN); 814 exit(STATE_UNKNOWN);
732 } 815 }
733 816
@@ -738,7 +821,8 @@ static int create_dhcp_socket(void) {
738 821
739 /* bind the socket */ 822 /* bind the socket */
740 if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) { 823 if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) {
741 printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"), DHCP_CLIENT_PORT); 824 printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"),
825 DHCP_CLIENT_PORT);
742 exit(STATE_UNKNOWN); 826 exit(STATE_UNKNOWN);
743 } 827 }
744 828
@@ -746,105 +830,121 @@ static int create_dhcp_socket(void) {
746} 830}
747 831
748/* closes DHCP socket */ 832/* closes DHCP socket */
749static int close_dhcp_socket(int sock) { 833int close_dhcp_socket(int sock) {
750
751 close(sock); 834 close(sock);
752
753 return OK; 835 return OK;
754} 836}
755 837
756/* adds a requested server address to list in memory */ 838/* adds a requested server address to list in memory */
757static int add_requested_server(struct in_addr server_address) { 839int add_requested_server(struct in_addr server_address, int *requested_servers,
758 requested_server *new_server; 840 requested_server **requested_server_list) {
759 841 requested_server *new_server = (requested_server *)malloc(sizeof(requested_server));
760 new_server = (requested_server *)malloc(sizeof(requested_server)); 842 if (new_server == NULL) {
761 if (new_server == NULL)
762 return ERROR; 843 return ERROR;
844 }
763 845
764 new_server->server_address = server_address; 846 new_server->server_address = server_address;
765 new_server->answered = false; 847 new_server->answered = false;
766 848
767 new_server->next = requested_server_list; 849 new_server->next = *requested_server_list;
768 requested_server_list = new_server; 850 *requested_server_list = new_server;
769 851
770 requested_servers++; 852 *requested_servers += 1;
771 853
772 if (verbose) 854 if (verbose) {
773 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); 855 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address));
856 }
774 857
775 return OK; 858 return OK;
776} 859}
777 860
778/* adds a DHCP OFFER to list in memory */ 861/* adds a DHCP OFFER to list in memory */
779static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { 862add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet,
863 dhcp_offer *dhcp_offer_list) {
864 if (offer_packet == NULL) {
865 add_dhcp_offer_wrapper tmp = {
866 .error = ERROR,
867 };
868 return tmp;
869 }
870
871 uint32_t dhcp_lease_time = 0;
872 uint32_t dhcp_renewal_time = 0;
873 uint32_t dhcp_rebinding_time = 0;
780 dhcp_offer *new_offer; 874 dhcp_offer *new_offer;
781 int x;
782 unsigned option_type;
783 unsigned option_length;
784 struct in_addr serv_ident = {0}; 875 struct in_addr serv_ident = {0};
785
786 if (offer_packet == NULL)
787 return ERROR;
788
789 /* process all DHCP options present in the packet */ 876 /* process all DHCP options present in the packet */
790 for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { 877 for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) {
791 878
792 if ((int)offer_packet->options[x] == -1) 879 if ((int)offer_packet->options[dchp_opt_idx] == -1) {
793 break; 880 break;
881 }
794 882
795 /* get option type */ 883 /* get option type */
796 option_type = offer_packet->options[x++]; 884 unsigned option_type = offer_packet->options[dchp_opt_idx++];
797 885
798 /* get option length */ 886 /* get option length */
799 option_length = offer_packet->options[x++]; 887 unsigned option_length = offer_packet->options[dchp_opt_idx++];
800 888
801 if (verbose) 889 if (verbose) {
802 printf("Option: %d (0x%02X)\n", option_type, option_length); 890 printf("Option: %d (0x%02X)\n", option_type, option_length);
891 }
803 892
804 /* get option data */ 893 /* get option data */
805 switch (option_type) { 894 switch (option_type) {
806 case DHCP_OPTION_LEASE_TIME: 895 case DHCP_OPTION_LEASE_TIME:
807 memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); 896 memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time));
808 dhcp_lease_time = ntohl(dhcp_lease_time); 897 dhcp_lease_time = ntohl(dhcp_lease_time);
809 break; 898 break;
810 case DHCP_OPTION_RENEWAL_TIME: 899 case DHCP_OPTION_RENEWAL_TIME:
811 memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); 900 memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx],
901 sizeof(dhcp_renewal_time));
812 dhcp_renewal_time = ntohl(dhcp_renewal_time); 902 dhcp_renewal_time = ntohl(dhcp_renewal_time);
813 break; 903 break;
814 case DHCP_OPTION_REBINDING_TIME: 904 case DHCP_OPTION_REBINDING_TIME:
815 memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); 905 memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx],
906 sizeof(dhcp_rebinding_time));
816 dhcp_rebinding_time = ntohl(dhcp_rebinding_time); 907 dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
817 break; 908 break;
818 case DHCP_OPTION_SERVER_IDENTIFIER: 909 case DHCP_OPTION_SERVER_IDENTIFIER:
819 memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); 910 memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx],
911 sizeof(serv_ident.s_addr));
820 break; 912 break;
821 } 913 }
822 914
823 /* skip option data we're ignoring */ 915 /* skip option data we're ignoring */
824 if (option_type == 0) /* "pad" option, see RFC 2132 (3.1) */ 916 if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */
825 x += 1; 917 dchp_opt_idx += 1;
826 else 918 } else {
827 x += option_length; 919 dchp_opt_idx += option_length;
920 }
828 } 921 }
829 922
830 if (verbose) { 923 if (verbose) {
831 if (dhcp_lease_time == DHCP_INFINITE_TIME) 924 if (dhcp_lease_time == DHCP_INFINITE_TIME) {
832 printf(_("Lease Time: Infinite\n")); 925 printf(_("Lease Time: Infinite\n"));
833 else 926 } else {
834 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); 927 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time);
835 if (dhcp_renewal_time == DHCP_INFINITE_TIME) 928 }
929 if (dhcp_renewal_time == DHCP_INFINITE_TIME) {
836 printf(_("Renewal Time: Infinite\n")); 930 printf(_("Renewal Time: Infinite\n"));
837 else 931 } else {
838 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); 932 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time);
839 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) 933 }
934 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) {
840 printf(_("Rebinding Time: Infinite\n")); 935 printf(_("Rebinding Time: Infinite\n"));
936 }
841 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); 937 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time);
842 } 938 }
843 939
844 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); 940 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer));
845 941
846 if (new_offer == NULL) 942 if (new_offer == NULL) {
847 return ERROR; 943 add_dhcp_offer_wrapper tmp = {
944 .error = ERROR,
945 };
946 return tmp;
947 }
848 948
849 /* 949 /*
850 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the 950 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the
@@ -874,15 +974,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
874 new_offer->next = dhcp_offer_list; 974 new_offer->next = dhcp_offer_list;
875 dhcp_offer_list = new_offer; 975 dhcp_offer_list = new_offer;
876 976
877 return OK; 977 add_dhcp_offer_wrapper result = {
978 .error = OK,
979 .dhcp_offer_list = dhcp_offer_list,
980 };
981
982 return result;
878} 983}
879 984
880/* frees memory allocated to DHCP OFFER list */ 985/* frees memory allocated to DHCP OFFER list */
881static int free_dhcp_offer_list(void) { 986int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) {
882 dhcp_offer *this_offer;
883 dhcp_offer *next_offer; 987 dhcp_offer *next_offer;
884 988 for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
885 for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
886 next_offer = this_offer->next; 989 next_offer = this_offer->next;
887 free(this_offer); 990 free(this_offer);
888 } 991 }
@@ -891,11 +994,10 @@ static int free_dhcp_offer_list(void) {
891} 994}
892 995
893/* frees memory allocated to requested server list */ 996/* frees memory allocated to requested server list */
894static int free_requested_server_list(void) { 997int free_requested_server_list(requested_server *requested_server_list) {
895 requested_server *this_server;
896 requested_server *next_server; 998 requested_server *next_server;
897 999 for (requested_server *this_server = requested_server_list; this_server != NULL;
898 for (this_server = requested_server_list; this_server != NULL; this_server = next_server) { 1000 this_server = next_server) {
899 next_server = this_server->next; 1001 next_server = this_server->next;
900 free(this_server); 1002 free(this_server);
901 } 1003 }
@@ -904,39 +1006,68 @@ static int free_requested_server_list(void) {
904} 1006}
905 1007
906/* gets state and plugin output to return */ 1008/* gets state and plugin output to return */
907static int get_results(void) { 1009mp_subcheck get_results(bool exclusive, const int requested_servers,
908 dhcp_offer *temp_offer, *undesired_offer = NULL; 1010 const struct in_addr requested_address, bool request_specific_address,
909 requested_server *temp_server; 1011 requested_server *requested_server_list, int valid_responses,
910 int result; 1012 dhcp_offer *dhcp_offer_list) {
911 uint32_t max_lease_time = 0; 1013 mp_subcheck sc_dhcp_results = mp_subcheck_init();
1014 sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK);
912 1015
913 received_requested_address = false; 1016 /* we didn't receive any DHCPOFFERs */
914 1017 if (dhcp_offer_list == NULL) {
915 /* checks responses from requested servers */ 1018 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
916 requested_responses = 0; 1019 xasprintf(&sc_dhcp_results.output, "%s", "No DHCPOFFERs were received");
917 if (requested_servers > 0) { 1020 return sc_dhcp_results;
1021 }
918 1022
919 for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { 1023 if (valid_responses == 0) {
1024 // No valid responses at all, so early exit here
1025 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
1026 xasprintf(&sc_dhcp_results.output, "No valid responses received");
1027 return sc_dhcp_results;
1028 }
920 1029
921 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1030 if (valid_responses == 1) {
1031 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses);
1032 } else {
1033 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses);
1034 }
922 1035
1036 bool received_requested_address = false;
1037 dhcp_offer *undesired_offer = NULL;
1038 uint32_t max_lease_time = 0;
1039 /* checks responses from requested servers */
1040 int requested_responses = 0;
1041 if (requested_servers > 0) {
1042 for (requested_server *temp_server = requested_server_list; temp_server != NULL;
1043 temp_server = temp_server->next) {
1044 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL;
1045 temp_offer = temp_offer->next) {
923 /* get max lease time we were offered */ 1046 /* get max lease time we were offered */
924 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1047 if (temp_offer->lease_time > max_lease_time ||
1048 temp_offer->lease_time == DHCP_INFINITE_TIME) {
925 max_lease_time = temp_offer->lease_time; 1049 max_lease_time = temp_offer->lease_time;
1050 }
926 1051
927 /* see if we got the address we requested */ 1052 /* see if we got the address we requested */
928 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1053 if (!memcmp(&requested_address, &temp_offer->offered_address,
1054 sizeof(requested_address))) {
929 received_requested_address = true; 1055 received_requested_address = true;
1056 }
930 1057
931 /* see if the servers we wanted a response from talked to us or not */ 1058 /* see if the servers we wanted a response from, talked to us or not */
932 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { 1059 if (!memcmp(&temp_offer->server_address, &temp_server->server_address,
1060 sizeof(temp_server->server_address))) {
933 if (verbose) { 1061 if (verbose) {
934 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); 1062 printf(_("DHCP Server Match: Offerer=%s"),
1063 inet_ntoa(temp_offer->server_address));
935 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); 1064 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address));
936 if (temp_server->answered) 1065 if (temp_server->answered) {
937 printf(_(" (duplicate)")); 1066 printf(_(" (duplicate)"));
1067 }
938 printf(_("\n")); 1068 printf(_("\n"));
939 } 1069 }
1070
940 if (!temp_server->answered) { 1071 if (!temp_server->answered) {
941 requested_responses++; 1072 requested_responses++;
942 temp_server->answered = true; 1073 temp_server->answered = true;
@@ -947,160 +1078,188 @@ static int get_results(void) {
947 } 1078 }
948 1079
949 /* exclusive mode: check for undesired offers */ 1080 /* exclusive mode: check for undesired offers */
950 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1081 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL;
1082 temp_offer = temp_offer->next) {
951 if (!temp_offer->desired) { 1083 if (!temp_offer->desired) {
952 undesired_offer = temp_offer; /* Checks only for the first undesired offer */ 1084 undesired_offer = temp_offer; /* Checks only for the first undesired offer */
953 break; /* no further checks needed */ 1085 break; /* no further checks needed */
954 } 1086 }
955 } 1087 }
956 }
957 1088
958 /* else check and see if we got our requested address from any server */ 1089 mp_subcheck sc_rqust_srvs = mp_subcheck_init();
959 else { 1090 xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded",
1091 requested_responses, requested_servers);
960 1092
961 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1093 if (requested_responses == requested_servers) {
1094 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK);
1095 } else if (requested_responses == 0) {
1096 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL);
1097 } else if (requested_responses < requested_servers) {
1098 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1099 } else {
1100 // We received more(!) responses than we asked for?
1101 // This case shouldn't happen, but is here for completion
1102 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1103 }
1104 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs);
962 1105
1106 } else {
1107 /* else check and see if we got our requested address from any server */
1108 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL;
1109 temp_offer = temp_offer->next) {
963 /* get max lease time we were offered */ 1110 /* get max lease time we were offered */
964 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1111 if (temp_offer->lease_time > max_lease_time ||
1112 temp_offer->lease_time == DHCP_INFINITE_TIME) {
965 max_lease_time = temp_offer->lease_time; 1113 max_lease_time = temp_offer->lease_time;
1114 }
966 1115
967 /* see if we got the address we requested */ 1116 /* see if we got the address we requested */
968 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1117 if (!memcmp(&requested_address, &temp_offer->offered_address,
1118 sizeof(requested_address))) {
969 received_requested_address = true; 1119 received_requested_address = true;
1120 }
970 } 1121 }
971 } 1122 }
972 1123
973 result = STATE_OK; 1124 if (max_lease_time == DHCP_INFINITE_TIME) {
974 if (valid_responses == 0) 1125 xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output);
975 result = STATE_CRITICAL; 1126 } else {
976 else if (requested_servers > 0 && requested_responses == 0) 1127 xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds",
977 result = STATE_CRITICAL; 1128 sc_dhcp_results.output, max_lease_time);
978 else if (requested_responses < requested_servers)
979 result = STATE_WARNING;
980 else if (request_specific_address && !received_requested_address)
981 result = STATE_WARNING;
982
983 if (exclusive && undesired_offer)
984 result = STATE_CRITICAL;
985
986 if (result == 0) /* garrett honeycutt 2005 */
987 printf("OK: ");
988 else if (result == 1)
989 printf("WARNING: ");
990 else if (result == 2)
991 printf("CRITICAL: ");
992 else if (result == 3)
993 printf("UNKNOWN: ");
994
995 /* we didn't receive any DHCPOFFERs */
996 if (dhcp_offer_list == NULL) {
997 printf(_("No DHCPOFFERs were received.\n"));
998 return result;
999 } 1129 }
1000 1130
1001 printf(_("Received %d DHCPOFFER(s)"), valid_responses); 1131 if (exclusive) {
1132 mp_subcheck sc_rogue_server = mp_subcheck_init();
1002 1133
1003 if (exclusive && undesired_offer) { 1134 if (undesired_offer != NULL) {
1004 printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); 1135 // We wanted to get a DHCPOFFER exclusively from one machine, but another one
1005 printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); 1136 // sent one (too)
1006 return result; 1137 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL);
1007 }
1008 1138
1009 if (requested_servers > 0) 1139 // Get the addresses for printout
1010 printf(_(", %s%d of %d requested servers responded"), ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, 1140 // 1.address of the sending server
1011 requested_servers); 1141 char server_address[INET_ADDRSTRLEN];
1142 const char *server_address_transformed = inet_ntop(
1143 AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address));
1012 1144
1013 if (request_specific_address) 1145 if (server_address != server_address_transformed) {
1014 printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); 1146 die(STATE_UNKNOWN, "inet_ntop failed");
1147 }
1015 1148
1016 printf(_(", max lease time = ")); 1149 // 2.address offered
1017 if (max_lease_time == DHCP_INFINITE_TIME) 1150 char offered_address[INET_ADDRSTRLEN];
1018 printf(_("Infinity")); 1151 const char *offered_address_transformed =
1019 else 1152 inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address,
1020 printf("%lu sec", (unsigned long)max_lease_time); 1153 sizeof(offered_address));
1021 1154
1022 printf(".\n"); 1155 if (offered_address != offered_address_transformed) {
1156 die(STATE_UNKNOWN, "inet_ntop failed");
1157 }
1023 1158
1024 return result; 1159 xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s",
1160 server_address, offered_address);
1161 } else {
1162 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK);
1163 xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected");
1164 }
1165 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server);
1166 }
1167
1168 if (request_specific_address) {
1169 mp_subcheck sc_rqustd_addr = mp_subcheck_init();
1170
1171 if (received_requested_address) {
1172 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK);
1173 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered",
1174 inet_ntoa(requested_address));
1175 } else {
1176 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING);
1177 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered",
1178 inet_ntoa(requested_address));
1179 }
1180
1181 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr);
1182 }
1183
1184 return sc_dhcp_results;
1025} 1185}
1026 1186
1027/* process command-line arguments */ 1187/* process command-line arguments */
1028static int process_arguments(int argc, char **argv) { 1188process_arguments_wrapper process_arguments(int argc, char **argv) {
1029 if (argc < 1) 1189 if (argc < 1) {
1030 return ERROR; 1190 process_arguments_wrapper tmp = {
1191 .error = ERROR,
1192 };
1193 return tmp;
1194 }
1031 1195
1032 call_getopt(argc, argv); 1196 enum {
1033 return validate_arguments(argc); 1197 output_format_index = CHAR_MAX + 1,
1034} 1198 };
1035 1199
1036static int call_getopt(int argc, char **argv) {
1037 extern int optind;
1038 int option_index = 0; 1200 int option_index = 0;
1039 static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, 1201 static struct option long_options[] = {
1040 {"requestedip", required_argument, 0, 'r'}, 1202 {"serverip", required_argument, 0, 's'},
1041 {"timeout", required_argument, 0, 't'}, 1203 {"requestedip", required_argument, 0, 'r'},
1042 {"interface", required_argument, 0, 'i'}, 1204 {"timeout", required_argument, 0, 't'},
1043 {"mac", required_argument, 0, 'm'}, 1205 {"interface", required_argument, 0, 'i'},
1044 {"unicast", no_argument, 0, 'u'}, 1206 {"mac", required_argument, 0, 'm'},
1045 {"exclusive", no_argument, 0, 'x'}, 1207 {"unicast", no_argument, 0, 'u'},
1046 {"verbose", no_argument, 0, 'v'}, 1208 {"exclusive", no_argument, 0, 'x'},
1047 {"version", no_argument, 0, 'V'}, 1209 {"verbose", no_argument, 0, 'v'},
1048 {"help", no_argument, 0, 'h'}, 1210 {"version", no_argument, 0, 'V'},
1049 {0, 0, 0, 0}}; 1211 {"help", no_argument, 0, 'h'},
1050 1212 {"output-format", required_argument, 0, output_format_index},
1051 int c = 0; 1213 {0, 0, 0, 0}};
1214
1215 check_dhcp_config config = check_dhcp_config_init();
1216 int option_char = 0;
1052 while (true) { 1217 while (true) {
1053 c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); 1218 option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index);
1054 1219
1055 if (c == -1 || c == EOF || c == 1) 1220 if (option_char == -1 || option_char == EOF || option_char == 1) {
1056 break; 1221 break;
1222 }
1057 1223
1058 switch (c) { 1224 switch (option_char) {
1059
1060 case 's': /* DHCP server address */ 1225 case 's': /* DHCP server address */
1061 resolve_host(optarg, &dhcp_ip); 1226 resolve_host(optarg, &config.dhcp_ip);
1062 add_requested_server(dhcp_ip); 1227 add_requested_server(config.dhcp_ip, &config.num_of_requested_servers,
1228 &config.requested_server_list);
1063 break; 1229 break;
1064 1230
1065 case 'r': /* address we are requested from DHCP servers */ 1231 case 'r': /* address we are requested from DHCP servers */
1066 resolve_host(optarg, &requested_address); 1232 resolve_host(optarg, &config.requested_address);
1067 request_specific_address = true; 1233 config.request_specific_address = true;
1068 break; 1234 break;
1069 1235
1070 case 't': /* timeout */ 1236 case 't': /* timeout */
1071 1237 if (atoi(optarg) > 0) {
1072 /* 1238 config.dhcpoffer_timeout = atoi(optarg);
1073 if(is_intnonneg(optarg)) 1239 }
1074 */
1075 if (atoi(optarg) > 0)
1076 dhcpoffer_timeout = atoi(optarg);
1077 /*
1078 else
1079 usage("Time interval must be a nonnegative integer\n");
1080 */
1081 break; 1240 break;
1082 1241
1083 case 'm': /* MAC address */ 1242 case 'm': /* MAC address */
1084 1243 if ((config.user_specified_mac = mac_aton(optarg)) == NULL) {
1085 if ((user_specified_mac = mac_aton(optarg)) == NULL)
1086 usage("Cannot parse MAC address.\n"); 1244 usage("Cannot parse MAC address.\n");
1087 if (verbose) 1245 }
1088 print_hardware_address(user_specified_mac); 1246 if (verbose) {
1089 1247 print_hardware_address(config.user_specified_mac);
1248 }
1090 break; 1249 break;
1091 1250
1092 case 'i': /* interface name */ 1251 case 'i': /* interface name */
1093 1252 strncpy(config.network_interface_name, optarg,
1094 strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); 1253 sizeof(config.network_interface_name) - 1);
1095 network_interface_name[sizeof(network_interface_name) - 1] = '\x0'; 1254 config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0';
1096
1097 break; 1255 break;
1098 1256
1099 case 'u': /* unicast testing */ 1257 case 'u': /* unicast testing */
1100 unicast = true; 1258 config.unicast_mode = true;
1101 break; 1259 break;
1260
1102 case 'x': /* exclusive testing aka "rogue DHCP server detection" */ 1261 case 'x': /* exclusive testing aka "rogue DHCP server detection" */
1103 exclusive = true; 1262 config.exclusive_mode = true;
1104 break; 1263 break;
1105 1264
1106 case 'V': /* version */ 1265 case 'V': /* version */
@@ -1114,6 +1273,18 @@ static int call_getopt(int argc, char **argv) {
1114 case 'v': /* verbose */ 1273 case 'v': /* verbose */
1115 verbose = 1; 1274 verbose = 1;
1116 break; 1275 break;
1276 case output_format_index: {
1277 parsed_output_format parser = mp_parse_output_format(optarg);
1278 if (!parser.parsing_success) {
1279 // TODO List all available formats here, maybe add anothoer usage function
1280 printf("Invalid output format: %s\n", optarg);
1281 exit(STATE_UNKNOWN);
1282 }
1283
1284 config.output_format_is_set = true;
1285 config.output_format = parser.output_format;
1286 break;
1287 }
1117 case '?': /* help */ 1288 case '?': /* help */
1118 usage5(); 1289 usage5();
1119 break; 1290 break;
@@ -1122,15 +1293,16 @@ static int call_getopt(int argc, char **argv) {
1122 break; 1293 break;
1123 } 1294 }
1124 } 1295 }
1125 return optind;
1126}
1127 1296
1128static int validate_arguments(int argc) { 1297 if (argc - optind > 0) {
1129
1130 if (argc - optind > 0)
1131 usage(_("Got unexpected non-option argument")); 1298 usage(_("Got unexpected non-option argument"));
1299 }
1132 1300
1133 return OK; 1301 process_arguments_wrapper result = {
1302 .config = config,
1303 .error = OK,
1304 };
1305 return result;
1134} 1306}
1135 1307
1136#if defined(__sun__) || defined(__solaris__) || defined(__hpux__) 1308#if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
@@ -1180,7 +1352,8 @@ static int put_ctrl(int fd, int len, int pri) {
1180 1352
1181 ctl.len = len; 1353 ctl.len = len;
1182 if (putmsg(fd, &ctl, 0, pri) < 0) { 1354 if (putmsg(fd, &ctl, 0, pri) < 0) {
1183 printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), strerror(errno)); 1355 printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"),
1356 strerror(errno));
1184 exit(STATE_UNKNOWN); 1357 exit(STATE_UNKNOWN);
1185 } 1358 }
1186 1359
@@ -1193,7 +1366,8 @@ static int put_both(int fd, int clen, int dlen, int pri) {
1193 ctl.len = clen; 1366 ctl.len = clen;
1194 dat.len = dlen; 1367 dat.len = dlen;
1195 if (putmsg(fd, &ctl, &dat, pri) < 0) { 1368 if (putmsg(fd, &ctl, &dat, pri) < 0) {
1196 printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), strerror(errno)); 1369 printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"),
1370 strerror(errno));
1197 exit(STATE_UNKNOWN); 1371 exit(STATE_UNKNOWN);
1198 } 1372 }
1199 1373
@@ -1205,7 +1379,8 @@ static int dl_open(const char *dev, int unit, int *fd) {
1205 dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area; 1379 dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area;
1206 1380
1207 if ((*fd = open(dev, O_RDWR)) == -1) { 1381 if ((*fd = open(dev, O_RDWR)) == -1) {
1208 printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), dev, strerror(errno)); 1382 printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"),
1383 dev, strerror(errno));
1209 exit(STATE_UNKNOWN); 1384 exit(STATE_UNKNOWN);
1210 } 1385 }
1211 attach_req->dl_primitive = DL_ATTACH_REQ; 1386 attach_req->dl_primitive = DL_ATTACH_REQ;
@@ -1229,7 +1404,8 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1229 put_ctrl(fd, sizeof(dl_bind_req_t), 0); 1404 put_ctrl(fd, sizeof(dl_bind_req_t), 0);
1230 get_msg(fd); 1405 get_msg(fd);
1231 if (GOT_ERR == check_ctrl(DL_BIND_ACK)) { 1406 if (GOT_ERR == check_ctrl(DL_BIND_ACK)) {
1232 printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), strerror(errno)); 1407 printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"),
1408 strerror(errno));
1233 exit(STATE_UNKNOWN); 1409 exit(STATE_UNKNOWN);
1234 } 1410 }
1235 bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length); 1411 bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length);
@@ -1249,7 +1425,7 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1249 * 1425 *
1250 ***********************************************************************/ 1426 ***********************************************************************/
1251 1427
1252static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { 1428long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1253 int fd; 1429 int fd;
1254 u_char mac_addr[25]; 1430 u_char mac_addr[25];
1255 1431
@@ -1268,51 +1444,53 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1268#endif 1444#endif
1269 1445
1270/* resolve host name or die (TODO: move this to netutils.c!) */ 1446/* resolve host name or die (TODO: move this to netutils.c!) */
1271static void resolve_host(const char *in, struct in_addr *out) { 1447void resolve_host(const char *name, struct in_addr *out) {
1272 struct addrinfo hints, *ai; 1448 struct addrinfo hints = {
1449 .ai_family = PF_INET,
1450 };
1451 struct addrinfo *addr_info;
1273 1452
1274 memset(&hints, 0, sizeof(hints)); 1453 if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) {
1275 hints.ai_family = PF_INET;
1276 if (getaddrinfo(in, NULL, &hints, &ai) != 0)
1277 usage_va(_("Invalid hostname/address - %s"), optarg); 1454 usage_va(_("Invalid hostname/address - %s"), optarg);
1455 }
1278 1456
1279 memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); 1457 memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out));
1280 freeaddrinfo(ai); 1458 freeaddrinfo(addr_info);
1281} 1459}
1282 1460
1283/* parse MAC address string, return 6 bytes (unterminated) or NULL */ 1461/* parse MAC address string, return 6 bytes (unterminated) or NULL */
1284static unsigned char *mac_aton(const char *string) { 1462unsigned char *mac_aton(const char *string) {
1285 static unsigned char result[6]; 1463 static unsigned char result[MAC_ADDR_LEN];
1286 char tmp[3]; 1464 char tmp[3];
1287 unsigned i, j; 1465 unsigned byte_counter = 0;
1288 1466
1289 for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { 1467 for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) {
1290 /* ignore ':' and any other non-hex character */ 1468 /* ignore ':' and any other non-hex character */
1291 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) 1469 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) {
1292 continue; 1470 continue;
1471 }
1293 tmp[0] = string[i]; 1472 tmp[0] = string[i];
1294 tmp[1] = string[i + 1]; 1473 tmp[1] = string[i + 1];
1295 tmp[2] = '\0'; 1474 tmp[2] = '\0';
1296 result[j] = strtol(tmp, (char **)NULL, 16); 1475 result[byte_counter] = strtol(tmp, (char **)NULL, 16);
1297 i++; 1476 i++;
1298 j++; 1477 byte_counter++;
1299 } 1478 }
1300 1479
1301 return (j == 6) ? result : NULL; 1480 return (byte_counter == MAC_ADDR_LEN) ? result : NULL;
1302} 1481}
1303 1482
1304static void print_hardware_address(const unsigned char *address) { 1483void print_hardware_address(const unsigned char *address) {
1305 int i;
1306 1484
1307 printf(_("Hardware address: ")); 1485 printf(_("Hardware address: "));
1308 for (i = 0; i < 5; i++) 1486 for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) {
1309 printf("%2.2x:", address[i]); 1487 printf("%2.2x:", address[addr_idx]);
1310 printf("%2.2x", address[i]); 1488 }
1311 putchar('\n'); 1489 putchar('\n');
1312} 1490}
1313 1491
1314/* print usage help */ 1492/* print usage help */
1315static void print_help(void) { 1493void print_help(void) {
1316 1494
1317 print_revision(progname, NP_VERSION); 1495 print_revision(progname, NP_VERSION);
1318 1496
@@ -1328,6 +1506,7 @@ static void print_help(void) {
1328 printf(UT_HELP_VRSN); 1506 printf(UT_HELP_VRSN);
1329 printf(UT_EXTRA_OPTS); 1507 printf(UT_EXTRA_OPTS);
1330 1508
1509 printf(UT_OUTPUT_FORMAT);
1331 printf(UT_VERBOSE); 1510 printf(UT_VERBOSE);
1332 1511
1333 printf(" %s\n", "-s, --serverip=IPADDRESS"); 1512 printf(" %s\n", "-s, --serverip=IPADDRESS");
@@ -1343,10 +1522,10 @@ static void print_help(void) {
1343 printf(" %s\n", "-u, --unicast"); 1522 printf(" %s\n", "-u, --unicast");
1344 printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s")); 1523 printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s"));
1345 printf(" %s\n", "-x, --exclusive"); 1524 printf(" %s\n", "-x, --exclusive");
1346 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); 1525 printf(" %s\n",
1526 _("Only requested DHCP server may response (rogue DHCP server detection), requires -s"));
1347 1527
1348 printf(UT_SUPPORT); 1528 printf(UT_SUPPORT);
1349 return;
1350} 1529}
1351 1530
1352void print_usage(void) { 1531void print_usage(void) {
@@ -1354,6 +1533,4 @@ void print_usage(void) {
1354 printf("%s\n", _("Usage:")); 1533 printf("%s\n", _("Usage:"));
1355 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); 1534 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname);
1356 printf(" [-i interface] [-m mac]\n"); 1535 printf(" [-i interface] [-m mac]\n");
1357
1358 return;
1359} 1536}
diff --git a/plugins-root/check_dhcp.d/config.h b/plugins-root/check_dhcp.d/config.h
new file mode 100644
index 00000000..f189068b
--- /dev/null
+++ b/plugins-root/check_dhcp.d/config.h
@@ -0,0 +1,50 @@
1#pragma once
2
3#include "../../config.h"
4#include "../lib/states.h"
5#include <stdbool.h>
6#include <netinet/in.h>
7#include "net/if.h"
8#include "output.h"
9
10typedef struct requested_server_struct {
11 struct in_addr server_address;
12 bool answered;
13 struct requested_server_struct *next;
14} requested_server;
15
16typedef struct check_dhcp_config {
17 bool unicast_mode; /* unicast mode: mimic a DHCP relay */
18 bool exclusive_mode; /* exclusive mode aka "rogue DHCP server detection" */
19 int num_of_requested_servers;
20 struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
21 struct in_addr requested_address;
22 bool request_specific_address;
23
24 int dhcpoffer_timeout;
25 unsigned char *user_specified_mac;
26 char network_interface_name[IFNAMSIZ];
27 requested_server *requested_server_list;
28
29 mp_output_format output_format;
30 bool output_format_is_set;
31} check_dhcp_config;
32
33check_dhcp_config check_dhcp_config_init(void) {
34 check_dhcp_config tmp = {
35 .unicast_mode = false,
36 .exclusive_mode = false,
37 .num_of_requested_servers = 0,
38 .dhcp_ip = {0},
39 .requested_address = {0},
40 .request_specific_address = false,
41
42 .dhcpoffer_timeout = 2,
43 .user_specified_mac = NULL,
44 .network_interface_name = "eth0",
45 .requested_server_list = NULL,
46
47 .output_format_is_set = false,
48 };
49 return tmp;
50}
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index dcaceddb..d46d2ccc 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -46,6 +46,8 @@ const char *email = "devel@monitoring-plugins.org";
46#include "../plugins/common.h" 46#include "../plugins/common.h"
47#include "netutils.h" 47#include "netutils.h"
48#include "utils.h" 48#include "utils.h"
49#include "output.h"
50#include "perfdata.h"
49 51
50#if HAVE_SYS_SOCKIO_H 52#if HAVE_SYS_SOCKIO_H
51# include <sys/sockio.h> 53# include <sys/sockio.h>
@@ -65,6 +67,17 @@ const char *email = "devel@monitoring-plugins.org";
65#include <netinet/icmp6.h> 67#include <netinet/icmp6.h>
66#include <arpa/inet.h> 68#include <arpa/inet.h>
67#include <math.h> 69#include <math.h>
70#include <netdb.h>
71#include <sys/types.h>
72#include <unistd.h>
73#include <stdint.h>
74#include <sys/socket.h>
75#include <assert.h>
76#include <sys/select.h>
77
78#include "../lib/states.h"
79#include "./check_icmp.d/config.h"
80#include "./check_icmp.d/check_icmp_helpers.h"
68 81
69/** sometimes undefined system macros (quite a few, actually) **/ 82/** sometimes undefined system macros (quite a few, actually) **/
70#ifndef MAXTTL 83#ifndef MAXTTL
@@ -96,56 +109,8 @@ const char *email = "devel@monitoring-plugins.org";
96# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 109# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
97#endif 110#endif
98 111
99typedef unsigned short range_t; /* type for get_range() -- unimplemented */
100
101typedef struct rta_host {
102 unsigned short id; /* id in **table, and icmp pkts */
103 char *name; /* arg used for adding this host */
104 char *msg; /* icmp error message, if any */
105 struct sockaddr_storage saddr_in; /* the address of this host */
106 struct sockaddr_storage error_addr; /* stores address of error replies */
107 unsigned long long time_waited; /* total time waited, in usecs */
108 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
109 unsigned char icmp_type, icmp_code; /* type and code from errors */
110 unsigned short flags; /* control/status flags */
111 double rta; /* measured RTA */
112 int rta_status; // check result for RTA checks
113 double rtmax; /* max rtt */
114 double rtmin; /* min rtt */
115 double jitter; /* measured jitter */
116 int jitter_status; // check result for Jitter checks
117 double jitter_max; /* jitter rtt maximum */
118 double jitter_min; /* jitter rtt minimum */
119 double EffectiveLatency;
120 double mos; /* Mean opnion score */
121 int mos_status; // check result for MOS checks
122 double score; /* score */
123 int score_status; // check result for score checks
124 u_int last_tdiff;
125 u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
126 unsigned char pl; /* measured packet loss */
127 int pl_status; // check result for packet loss checks
128 struct rta_host *next; /* linked list */
129 int order_status; // check result for packet order checks
130} rta_host;
131
132#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */ 112#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
133 113
134/* threshold structure. all values are maximum allowed, exclusive */
135typedef struct threshold {
136 unsigned char pl; /* max allowed packet loss in percent */
137 unsigned int rta; /* roundtrip time average, microseconds */
138 double jitter; /* jitter time average, microseconds */
139 double mos; /* MOS */
140 double score; /* Score */
141} threshold;
142
143/* the data structure */
144typedef struct icmp_ping_data {
145 struct timeval stime; /* timestamp (saved in protocol struct as well) */
146 unsigned short ping_id;
147} icmp_ping_data;
148
149typedef union ip_hdr { 114typedef union ip_hdr {
150 struct ip ip; 115 struct ip ip;
151 struct ip6_hdr ip6; 116 struct ip6_hdr ip6;
@@ -158,24 +123,6 @@ typedef union icmp_packet {
158 u_short *cksum_in; 123 u_short *cksum_in;
159} icmp_packet; 124} icmp_packet;
160 125
161/* the different modes of this program are as follows:
162 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
163 * MODE_HOSTCHECK: Return immediately upon any sign of life
164 * In addition, sends packets to ALL addresses assigned
165 * to this host (as returned by gethostbyname() or
166 * gethostbyaddr() and expects one host only to be checked at
167 * a time. Therefore, any packet response what so ever will
168 * count as a sign of life, even when received outside
169 * crit.rta limit. Do not misspell any additional IP's.
170 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
171 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
172 * tcp and udp args does this)
173 */
174#define MODE_RTA 0
175#define MODE_HOSTCHECK 1
176#define MODE_ALL 2
177#define MODE_ICMP 3
178
179enum enum_threshold_mode { 126enum enum_threshold_mode {
180 const_rta_mode, 127 const_rta_mode,
181 const_packet_loss_mode, 128 const_packet_loss_mode,
@@ -186,89 +133,487 @@ enum enum_threshold_mode {
186 133
187typedef enum enum_threshold_mode threshold_mode; 134typedef enum enum_threshold_mode threshold_mode;
188 135
189/* the different ping types we can do
190 * TODO: investigate ARP ping as well */
191#define HAVE_ICMP 1
192#define HAVE_UDP 2
193#define HAVE_TCP 4
194#define HAVE_ARP 8
195
196#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
197#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
198#define IP_HDR_SIZE 20
199#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
200#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
201
202/* various target states */
203#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
204#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
205#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
206#define TSTATE_UNREACH 0x08
207
208/** prototypes **/ 136/** prototypes **/
209void print_help(void); 137void print_help();
210void print_usage(void); 138void print_usage(void);
211static u_int get_timevar(const char *); 139
212static u_int get_timevaldiff(struct timeval *, struct timeval *); 140/* Time related */
213static in_addr_t get_ip_address(const char *); 141typedef struct {
214static int wait_for_reply(int, u_int); 142 int error_code;
215static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); 143 time_t time_range;
216static int send_icmp_ping(int, struct rta_host *); 144} get_timevar_wrapper;
217static int get_threshold(char *str, threshold *th); 145static get_timevar_wrapper get_timevar(const char *str);
218static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); 146static time_t get_timevaldiff(struct timeval earlier, struct timeval later);
219static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); 147static time_t get_timevaldiff_to_now(struct timeval earlier);
220static void run_checks(void); 148
221static void set_source_ip(char *); 149static in_addr_t get_ip_address(const char *ifname);
222static int add_target(char *); 150static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family);
223static int add_target_ip(char *, struct sockaddr_storage *); 151
224static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); 152/* Receiving data */
225static void parse_address(struct sockaddr_storage *, char *, int); 153static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval,
226static unsigned short icmp_checksum(uint16_t *, size_t); 154 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
227static void finish(int); 155 ping_target **table, unsigned short packets,
228static void crash(const char *, ...); 156 unsigned short number_of_targets, check_icmp_state *program_state);
229 157
230/** external **/ 158typedef struct {
231extern int optind; 159 sa_family_t recv_proto;
232extern char *optarg; 160 ssize_t received;
233extern char **environ; 161} recvfrom_wto_wrapper;
162static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *buf, unsigned int len,
163 struct sockaddr *saddr, time_t *timeout,
164 struct timeval *received_timestamp);
165static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
166 time_t *target_interval, uint16_t sender_id, ping_target **table,
167 unsigned short packets, unsigned short number_of_targets,
168 check_icmp_state *program_state);
169
170/* Sending data */
171static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host,
172 unsigned short icmp_pkt_size, uint16_t sender_id,
173 check_icmp_state *program_state);
174
175/* Threshold related */
176typedef struct {
177 int errorcode;
178 check_icmp_threshold threshold;
179} get_threshold_wrapper;
180static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold);
181
182typedef struct {
183 int errorcode;
184 check_icmp_threshold warn;
185 check_icmp_threshold crit;
186} get_threshold2_wrapper;
187static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
188 check_icmp_threshold crit, threshold_mode mode);
189
190typedef struct {
191 int errorcode;
192 check_icmp_threshold result;
193} parse_threshold2_helper_wrapper;
194static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
195 size_t length,
196 check_icmp_threshold thr,
197 threshold_mode mode);
198
199/* main test function */
200static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
201 check_icmp_execution_mode mode, time_t max_completion_time,
202 struct timeval prog_start, ping_target **table, unsigned short packets,
203 check_icmp_socket_set sockset, unsigned short number_of_targets,
204 check_icmp_state *program_state);
205mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
206 check_icmp_threshold warn, check_icmp_threshold crit);
207
208typedef struct {
209 int targets_ok;
210 int targets_warn;
211 mp_subcheck sc_host;
212} evaluate_host_wrapper;
213evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
214 check_icmp_mode_switches modes, check_icmp_threshold warn,
215 check_icmp_threshold crit);
216
217/* Target acquisition */
218typedef struct {
219 int error_code;
220 check_icmp_target_container host;
221 bool has_v4;
222 bool has_v6;
223} add_host_wrapper;
224static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
225 sa_family_t enforced_proto);
226
227typedef struct {
228 int error_code;
229 ping_target *targets;
230 unsigned int number_of_targets;
231 bool has_v4;
232 bool has_v6;
233} add_target_wrapper;
234static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode,
235 sa_family_t enforced_proto);
236
237typedef struct {
238 int error_code;
239 ping_target *target;
240} add_target_ip_wrapper;
241static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address);
242
243static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size);
244
245static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size);
246
247/* End of run function */
248static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive,
249 check_icmp_threshold warn, check_icmp_threshold crit,
250 unsigned short number_of_targets, check_icmp_state *program_state,
251 check_icmp_target_container host_list[], unsigned short number_of_hosts,
252 mp_check overall[static 1]);
253
254/* Error exit */
255static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
234 256
235/** global variables **/ 257/** global variables **/
236static struct rta_host **table, *cursor, *list; 258static int debug = 0;
237 259
238static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 260extern unsigned int timeout;
239static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 261
240 262/** the working code **/
241static int mode, protocols, sockets, debug = 0, timeout = 10; 263static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) {
242static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; 264 return targets - targets_down;
243static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; 265}
244 266static inline unsigned int icmp_pkts_en_route(unsigned int icmp_sent, unsigned int icmp_recv,
245static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0; 267 unsigned int icmp_lost) {
246#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) 268 return icmp_sent - (icmp_recv + icmp_lost);
247static unsigned short targets_down = 0, targets = 0, packets = 0; 269}
248#define targets_alive (targets - targets_down) 270
249static unsigned int retry_interval, pkt_interval, target_interval; 271// Create configuration from cli parameters
250static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; 272typedef struct {
251static pid_t pid; 273 int errorcode;
252static struct timezone tz; 274 check_icmp_config config;
253static struct timeval prog_start; 275} check_icmp_config_wrapper;
254static unsigned long long max_completion_time = 0; 276check_icmp_config_wrapper process_arguments(int argc, char **argv) {
255static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ 277 /* get calling name the old-fashioned way for portability instead
256static int min_hosts_alive = -1; 278 * of relying on the glibc-ism __progname */
257static float pkt_backoff_factor = 1.5; 279 char *ptr = strrchr(argv[0], '/');
258static float target_backoff_factor = 1.5; 280 if (ptr) {
259static bool rta_mode = false; 281 progname = &ptr[1];
260static bool pl_mode = false; 282 } else {
261static bool jitter_mode = false; 283 progname = argv[0];
262static bool score_mode = false; 284 }
263static bool mos_mode = false; 285
264static bool order_mode = false; 286 check_icmp_config_wrapper result = {
287 .errorcode = OK,
288 .config = check_icmp_config_init(),
289 };
290
291 /* use the pid to mark packets as ours */
292 /* Some systems have 32-bit pid_t so mask off only 16 bits */
293 result.config.sender_id = getpid() & 0xffff;
294
295 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
296 result.config.mode = MODE_ICMP;
297 } else if (!strcmp(progname, "check_host")) {
298 result.config.mode = MODE_HOSTCHECK;
299 result.config.number_of_packets = 5;
300 result.config.crit.rta = result.config.warn.rta = 1000000;
301 result.config.crit.pl = result.config.warn.pl = 100;
302 } else if (!strcmp(progname, "check_rta_multi")) {
303 result.config.mode = MODE_ALL;
304 result.config.target_interval = 0;
305 result.config.number_of_packets = 5;
306 }
307 /* support "--help" and "--version" */
308 if (argc == 2) {
309 if (!strcmp(argv[1], "--help")) {
310 strcpy(argv[1], "-h");
311 }
312 if (!strcmp(argv[1], "--version")) {
313 strcpy(argv[1], "-V");
314 }
315 }
316
317 sa_family_t enforced_ai_family = AF_UNSPEC;
318
319 enum {
320 output_format_index = CHAR_MAX + 1,
321 };
322
323 struct option longopts[] = {
324 {"version", no_argument, 0, 'V'},
325 {"help", no_argument, 0, 'h'},
326 {"verbose", no_argument, 0, 'v'},
327 {"Host", required_argument, 0, 'H'},
328 {"ipv4-only", no_argument, 0, '4'},
329 {"ipv6-only", no_argument, 0, '6'},
330 {"warning", required_argument, 0, 'w'},
331 {"critical", required_argument, 0, 'c'},
332 {"rta-mode-thresholds", required_argument, 0, 'R'},
333 {"packet-loss-mode-thresholds", required_argument, 0, 'P'},
334 {"jitter-mode-thresholds", required_argument, 0, 'J'},
335 {"mos-mode-thresholds", required_argument, 0, 'M'},
336 {"score-mode-thresholds", required_argument, 0, 'S'},
337 {"out-of-order-packets", no_argument, 0, 'O'},
338 {"number-of-packets", required_argument, 0, 'n'},
339 {"number-of-packets", required_argument, 0, 'p'},
340 {"packet-interval", required_argument, 0, 'i'},
341 {"target-interval", required_argument, 0, 'I'},
342 {"minimal-host-alive", required_argument, 0, 'm'},
343 {"outgoing-ttl", required_argument, 0, 'l'},
344 {"size", required_argument, 0, 'b'},
345 {"output-format", required_argument, 0, output_format_index},
346 {},
347 };
348
349 // Parse protocol arguments first
350 // and count hosts here
351 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
352 for (int i = 1; i < argc; i++) {
353 long int arg;
354 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
355 switch (arg) {
356
357 case '4':
358 if (enforced_ai_family != AF_UNSPEC) {
359 crash("Multiple protocol versions not supported");
360 }
361 enforced_ai_family = AF_INET;
362 break;
363 case '6':
364 if (enforced_ai_family != AF_UNSPEC) {
365 crash("Multiple protocol versions not supported");
366 }
367 enforced_ai_family = AF_INET6;
368 break;
369 case 'H': {
370 result.config.number_of_hosts++;
371 break;
372 }
373 case 'h': /* help */
374 // Trigger help here to avoid adding hosts before that (and doing DNS queries)
375 print_help();
376 exit(STATE_UNKNOWN);
377 break;
378 case 'v':
379 debug++;
380 break;
381 }
382 }
383 }
384
385 char **tmp = &argv[optind];
386 while (*tmp) {
387 result.config.number_of_hosts++;
388 tmp++;
389 }
390
391 // Sanity check: if hostmode is selected,only a single host is allowed
392 if (result.config.mode == MODE_HOSTCHECK && result.config.number_of_hosts > 1) {
393 usage("check_host only allows a single host");
394 }
395
396 // Allocate hosts
397 result.config.hosts =
398 calloc(result.config.number_of_hosts, sizeof(check_icmp_target_container));
399 if (result.config.hosts == NULL) {
400 crash("failed to allocate memory");
401 }
402
403 /* Reset argument scanning */
404 optind = 1;
405
406 int host_counter = 0;
407 /* parse the arguments */
408 for (int i = 1; i < argc; i++) {
409 long int arg;
410 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
411 switch (arg) {
412 case 'b': {
413 long size = strtol(optarg, NULL, 0);
414 if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
415 size < MAX_PING_DATA) {
416 result.config.icmp_data_size = (unsigned short)size;
417 } else {
418 usage_va("ICMP data length must be between: %lu and %lu",
419 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
420 MAX_PING_DATA - 1);
421 }
422 } break;
423 case 'i': {
424 // packet_interval was unused and is now removed
425 } break;
426 case 'I': {
427 get_timevar_wrapper parsed_time = get_timevar(optarg);
428
429 if (parsed_time.error_code == OK) {
430 result.config.target_interval = parsed_time.time_range;
431 } else {
432 crash("failed to parse target interval");
433 }
434 } break;
435 case 'w': {
436 get_threshold_wrapper warn = get_threshold(optarg, result.config.warn);
437 if (warn.errorcode == OK) {
438 result.config.warn = warn.threshold;
439 } else {
440 crash("failed to parse warning threshold");
441 }
442 } break;
443 case 'c': {
444 get_threshold_wrapper crit = get_threshold(optarg, result.config.crit);
445 if (crit.errorcode == OK) {
446 result.config.crit = crit.threshold;
447 } else {
448 crash("failed to parse critical threshold");
449 }
450 } break;
451 case 'n':
452 case 'p':
453 result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0);
454 if (result.config.number_of_packets > 20) {
455 errno = 0;
456 crash("packets is > 20 (%d)", result.config.number_of_packets);
457 }
458 break;
459 case 't':
460 // WARNING Deprecated since execution time is determined by the other factors
461 break;
462 case 'H': {
463 add_host_wrapper host_add_result =
464 add_host(optarg, result.config.mode, enforced_ai_family);
465 if (host_add_result.error_code == OK) {
466 result.config.hosts[host_counter] = host_add_result.host;
467 host_counter++;
468
469 if (result.config.targets != NULL) {
470 result.config.number_of_targets += ping_target_list_append(
471 result.config.targets, host_add_result.host.target_list);
472 } else {
473 result.config.targets = host_add_result.host.target_list;
474 result.config.number_of_targets += host_add_result.host.number_of_targets;
475 }
476
477 if (host_add_result.has_v4) {
478 result.config.need_v4 = true;
479 }
480 if (host_add_result.has_v6) {
481 result.config.need_v6 = true;
482 }
483 } else {
484 crash("Failed to add host, unable to parse it correctly");
485 }
486 } break;
487 case 'l':
488 result.config.ttl = strtoul(optarg, NULL, 0);
489 break;
490 case 'm':
491 result.config.min_hosts_alive = (int)strtoul(optarg, NULL, 0);
492 break;
493 case 's': /* specify source IP address */
494 result.config.source_ip = optarg;
495 break;
496 case 'V': /* version */
497 print_revision(progname, NP_VERSION);
498 exit(STATE_UNKNOWN);
499 case 'R': /* RTA mode */ {
500 get_threshold2_wrapper rta_th = get_threshold2(
501 optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode);
502
503 if (rta_th.errorcode != OK) {
504 crash("Failed to parse RTA threshold");
505 }
506
507 result.config.warn = rta_th.warn;
508 result.config.crit = rta_th.crit;
509 result.config.modes.rta_mode = true;
510 } break;
511 case 'P': /* packet loss mode */ {
512 get_threshold2_wrapper pl_th =
513 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
514 const_packet_loss_mode);
515 if (pl_th.errorcode != OK) {
516 crash("Failed to parse packet loss threshold");
517 }
518
519 result.config.warn = pl_th.warn;
520 result.config.crit = pl_th.crit;
521 result.config.modes.pl_mode = true;
522 } break;
523 case 'J': /* jitter mode */ {
524 get_threshold2_wrapper jitter_th =
525 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
526 const_jitter_mode);
527 if (jitter_th.errorcode != OK) {
528 crash("Failed to parse jitter threshold");
529 }
530
531 result.config.warn = jitter_th.warn;
532 result.config.crit = jitter_th.crit;
533 result.config.modes.jitter_mode = true;
534 } break;
535 case 'M': /* MOS mode */ {
536 get_threshold2_wrapper mos_th = get_threshold2(
537 optarg, strlen(optarg), result.config.warn, result.config.crit, const_mos_mode);
538 if (mos_th.errorcode != OK) {
539 crash("Failed to parse MOS threshold");
540 }
541
542 result.config.warn = mos_th.warn;
543 result.config.crit = mos_th.crit;
544 result.config.modes.mos_mode = true;
545 } break;
546 case 'S': /* score mode */ {
547 get_threshold2_wrapper score_th =
548 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
549 const_score_mode);
550 if (score_th.errorcode != OK) {
551 crash("Failed to parse score threshold");
552 }
553
554 result.config.warn = score_th.warn;
555 result.config.crit = score_th.crit;
556 result.config.modes.score_mode = true;
557 } break;
558 case 'O': /* out of order mode */
559 result.config.modes.order_mode = true;
560 break;
561 case output_format_index: {
562 parsed_output_format parser = mp_parse_output_format(optarg);
563 if (!parser.parsing_success) {
564 // TODO List all available formats here, maybe add anothoer usage function
565 printf("Invalid output format: %s\n", optarg);
566 exit(STATE_UNKNOWN);
567 }
568
569 result.config.output_format_is_set = true;
570 result.config.output_format = parser.output_format;
571 break;
572 }
573 }
574 }
575 }
576
577 argv = &argv[optind];
578 while (*argv) {
579 add_target(*argv, result.config.mode, enforced_ai_family);
580 argv++;
581 }
582
583 if (!result.config.number_of_targets) {
584 errno = 0;
585 crash("No hosts to check");
586 }
587
588 /* stupid users should be able to give whatever thresholds they want
589 * (nothing will break if they do), but some anal plugin maintainer
590 * will probably add some printf() thing here later, so it might be
591 * best to at least show them where to do it. ;) */
592 if (result.config.warn.pl > result.config.crit.pl) {
593 result.config.warn.pl = result.config.crit.pl;
594 }
595 if (result.config.warn.rta > result.config.crit.rta) {
596 result.config.warn.rta = result.config.crit.rta;
597 }
598 if (result.config.warn.jitter > result.config.crit.jitter) {
599 result.config.crit.jitter = result.config.warn.jitter;
600 }
601 if (result.config.warn.mos < result.config.crit.mos) {
602 result.config.warn.mos = result.config.crit.mos;
603 }
604 if (result.config.warn.score < result.config.crit.score) {
605 result.config.warn.score = result.config.crit.score;
606 }
607
608 return result;
609}
265 610
266/** code start **/ 611/** code start **/
267static void crash(const char *fmt, ...) { 612static void crash(const char *fmt, ...) {
268 va_list ap;
269 613
270 printf("%s: ", progname); 614 printf("%s: ", progname);
271 615
616 va_list ap;
272 va_start(ap, fmt); 617 va_start(ap, fmt);
273 vprintf(fmt, ap); 618 vprintf(fmt, ap);
274 va_end(ap); 619 va_end(ap);
@@ -385,18 +730,20 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm
385 return msg; 730 return msg;
386} 731}
387 732
388static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { 733static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
389 struct icmp p, sent_icmp; 734 time_t *target_interval, const uint16_t sender_id,
390 struct rta_host *host = NULL; 735 ping_target **table, unsigned short packets,
391 736 const unsigned short number_of_targets,
392 memcpy(&p, packet, sizeof(p)); 737 check_icmp_state *program_state) {
393 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { 738 struct icmp icmp_packet;
739 memcpy(&icmp_packet, packet, sizeof(icmp_packet));
740 if (icmp_packet.icmp_type == ICMP_ECHO && ntohs(icmp_packet.icmp_id) == sender_id) {
394 /* echo request from us to us (pinging localhost) */ 741 /* echo request from us to us (pinging localhost) */
395 return 0; 742 return 0;
396 } 743 }
397 744
398 if (debug) { 745 if (debug) {
399 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); 746 printf("handle_random_icmp(%p, %p)\n", (void *)&icmp_packet, (void *)addr);
400 } 747 }
401 748
402 /* only handle a few types, since others can't possibly be replies to 749 /* only handle a few types, since others can't possibly be replies to
@@ -409,14 +756,17 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
409 * TIMXCEED actually sends a proper icmp response we will have passed 756 * TIMXCEED actually sends a proper icmp response we will have passed
410 * too many hops to have a hope of reaching it later, in which case it 757 * too many hops to have a hope of reaching it later, in which case it
411 * indicates overconfidence in the network, poor routing or both. */ 758 * indicates overconfidence in the network, poor routing or both. */
412 if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { 759 if (icmp_packet.icmp_type != ICMP_UNREACH && icmp_packet.icmp_type != ICMP_TIMXCEED &&
760 icmp_packet.icmp_type != ICMP_SOURCEQUENCH && icmp_packet.icmp_type != ICMP_PARAMPROB) {
413 return 0; 761 return 0;
414 } 762 }
415 763
416 /* might be for us. At least it holds the original package (according 764 /* might be for us. At least it holds the original package (according
417 * to RFC 792). If it isn't, just ignore it */ 765 * to RFC 792). If it isn't, just ignore it */
766 struct icmp sent_icmp;
418 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); 767 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
419 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) { 768 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != sender_id ||
769 ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) {
420 if (debug) { 770 if (debug) {
421 printf("Packet is no response to a packet we sent\n"); 771 printf("Packet is no response to a packet we sent\n");
422 } 772 }
@@ -424,14 +774,15 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
424 } 774 }
425 775
426 /* it is indeed a response for us */ 776 /* it is indeed a response for us */
427 host = table[ntohs(sent_icmp.icmp_seq) / packets]; 777 ping_target *host = table[ntohs(sent_icmp.icmp_seq) / packets];
428 if (debug) { 778 if (debug) {
429 char address[INET6_ADDRSTRLEN]; 779 char address[INET6_ADDRSTRLEN];
430 parse_address(addr, address, sizeof(address)); 780 parse_address(addr, address, sizeof(address));
431 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); 781 printf("Received \"%s\" from %s for ICMP ECHO sent.\n",
782 get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address);
432 } 783 }
433 784
434 icmp_lost++; 785 program_state->icmp_lost++;
435 host->icmp_lost++; 786 host->icmp_lost++;
436 /* don't spend time on lost hosts any more */ 787 /* don't spend time on lost hosts any more */
437 if (host->flags & FLAG_LOST_CAUSE) { 788 if (host->flags & FLAG_LOST_CAUSE) {
@@ -440,305 +791,104 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
440 791
441 /* source quench means we're sending too fast, so increase the 792 /* source quench means we're sending too fast, so increase the
442 * interval and mark this packet lost */ 793 * interval and mark this packet lost */
443 if (p.icmp_type == ICMP_SOURCEQUENCH) { 794 if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) {
444 pkt_interval *= pkt_backoff_factor; 795 *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR);
445 target_interval *= target_backoff_factor;
446 } else { 796 } else {
447 targets_down++; 797 program_state->targets_down++;
448 host->flags |= FLAG_LOST_CAUSE; 798 host->flags |= FLAG_LOST_CAUSE;
449 } 799 }
450 host->icmp_type = p.icmp_type; 800 host->icmp_type = icmp_packet.icmp_type;
451 host->icmp_code = p.icmp_code; 801 host->icmp_code = icmp_packet.icmp_code;
452 host->error_addr = *addr; 802 host->error_addr = *addr;
453 803
454 return 0; 804 return 0;
455} 805}
456 806
457void parse_address(struct sockaddr_storage *addr, char *address, int size) { 807void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size) {
458 switch (address_family) { 808 switch (addr->ss_family) {
459 case AF_INET: 809 case AF_INET:
460 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); 810 inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, dst, size);
461 break; 811 break;
462 case AF_INET6: 812 case AF_INET6:
463 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); 813 inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, dst, size);
464 break; 814 break;
815 default:
816 assert(false);
465 } 817 }
466} 818}
467 819
468int main(int argc, char **argv) { 820int main(int argc, char **argv) {
469 int i;
470 char *ptr;
471 long int arg;
472 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
473 int result;
474 struct rta_host *host;
475#ifdef HAVE_SIGACTION
476 struct sigaction sig_action;
477#endif
478#ifdef SO_TIMESTAMP
479 int on = 1;
480#endif
481 char *source_ip = NULL;
482 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
483 setlocale(LC_ALL, ""); 821 setlocale(LC_ALL, "");
484 bindtextdomain(PACKAGE, LOCALEDIR); 822 bindtextdomain(PACKAGE, LOCALEDIR);
485 textdomain(PACKAGE); 823 textdomain(PACKAGE);
486 824
487 /* we only need to be setsuid when we get the sockets, so do 825 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
488 * that before pointer magic (esp. on network data) */ 826 environ = NULL;
489 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
490
491 address_family = -1;
492 int icmp_proto = IPPROTO_ICMP;
493 827
494 /* get calling name the old-fashioned way for portability instead 828 /* Parse extra opts if any */
495 * of relying on the glibc-ism __progname */ 829 argv = np_extra_opts(&argc, argv, progname);
496 ptr = strrchr(argv[0], '/');
497 if (ptr) {
498 progname = &ptr[1];
499 } else {
500 progname = argv[0];
501 }
502 830
503 /* now set defaults. Use progname to set them initially (allows for 831 check_icmp_config_wrapper tmp_config = process_arguments(argc, argv);
504 * superfast check_host program when target host is up */
505 cursor = list = NULL;
506 table = NULL;
507
508 mode = MODE_RTA;
509 /* Default critical thresholds */
510 crit.rta = 500000;
511 crit.pl = 80;
512 crit.jitter = 50;
513 crit.mos = 3;
514 crit.score = 70;
515 /* Default warning thresholds */
516 warn.rta = 200000;
517 warn.pl = 40;
518 warn.jitter = 40;
519 warn.mos = 3.5;
520 warn.score = 80;
521
522 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
523 pkt_interval = 80000; /* 80 msec packet interval by default */
524 packets = 5;
525 832
526 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { 833 if (tmp_config.errorcode != OK) {
527 mode = MODE_ICMP; 834 crash("failed to parse config");
528 protocols = HAVE_ICMP;
529 } else if (!strcmp(progname, "check_host")) {
530 mode = MODE_HOSTCHECK;
531 pkt_interval = 1000000;
532 packets = 5;
533 crit.rta = warn.rta = 1000000;
534 crit.pl = warn.pl = 100;
535 } else if (!strcmp(progname, "check_rta_multi")) {
536 mode = MODE_ALL;
537 target_interval = 0;
538 pkt_interval = 50000;
539 packets = 5;
540 } 835 }
541 836
542 /* support "--help" and "--version" */ 837 const check_icmp_config config = tmp_config.config;
543 if (argc == 2) {
544 if (!strcmp(argv[1], "--help")) {
545 strcpy(argv[1], "-h");
546 }
547 if (!strcmp(argv[1], "--version")) {
548 strcpy(argv[1], "-V");
549 }
550 }
551 838
552 /* Parse protocol arguments first */ 839 if (config.output_format_is_set) {
553 for (i = 1; i < argc; i++) { 840 mp_set_format(config.output_format);
554 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
555 switch (arg) {
556 case '4':
557 if (address_family != -1) {
558 crash("Multiple protocol versions not supported");
559 }
560 address_family = AF_INET;
561 break;
562 case '6':
563#ifdef USE_IPV6
564 if (address_family != -1) {
565 crash("Multiple protocol versions not supported");
566 }
567 address_family = AF_INET6;
568#else
569 usage(_("IPv6 support not available\n"));
570#endif
571 break;
572 }
573 }
574 } 841 }
575 842
576 /* Reset argument scanning */ 843 check_icmp_socket_set sockset = {
577 optind = 1; 844 .socket4 = -1,
845 .socket6 = -1,
846 };
578 847
579 unsigned long size; 848 if (config.need_v4) {
580 bool err; 849 sockset.socket4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
581 /* parse the arguments */ 850 if (sockset.socket4 == -1) {
582 for (i = 1; i < argc; i++) { 851 crash("Failed to obtain ICMP v4 socket");
583 while ((arg = getopt(argc, argv, opts_str)) != EOF) { 852 }
584 switch (arg) {
585 case 'v':
586 debug++;
587 break;
588 case 'b':
589 size = strtol(optarg, NULL, 0);
590 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) {
591 icmp_data_size = size;
592 icmp_pkt_size = size + ICMP_MINLEN;
593 } else {
594 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data),
595 MAX_PING_DATA - 1);
596 }
597 break;
598 case 'i':
599 pkt_interval = get_timevar(optarg);
600 break;
601 case 'I':
602 target_interval = get_timevar(optarg);
603 break;
604 case 'w':
605 get_threshold(optarg, &warn);
606 break;
607 case 'c':
608 get_threshold(optarg, &crit);
609 break;
610 case 'n':
611 case 'p':
612 packets = strtoul(optarg, NULL, 0);
613 break;
614 case 't':
615 timeout = strtoul(optarg, NULL, 0);
616 if (!timeout) {
617 timeout = 10;
618 }
619 break;
620 case 'H':
621 add_target(optarg);
622 break;
623 case 'l':
624 ttl = (int)strtoul(optarg, NULL, 0);
625 break;
626 case 'm':
627 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
628 break;
629 case 'd': /* implement later, for cluster checks */
630 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
631 if (ptr) {
632 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
633 }
634 break;
635 case 's': /* specify source IP address */
636 source_ip = optarg;
637 break;
638 case 'V': /* version */
639 print_revision(progname, NP_VERSION);
640 exit(STATE_UNKNOWN);
641 case 'h': /* help */
642 print_help();
643 exit(STATE_UNKNOWN);
644 break;
645 case 'R': /* RTA mode */
646 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
647 if (!err) {
648 crash("Failed to parse RTA threshold");
649 }
650 853
651 rta_mode = true; 854 if (config.source_ip) {
652 break;
653 case 'P': /* packet loss mode */
654 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
655 if (!err) {
656 crash("Failed to parse packet loss threshold");
657 }
658 855
659 pl_mode = true; 856 struct in_addr tmp = {};
660 break; 857 int error_code = inet_pton(AF_INET, config.source_ip, &tmp);
661 case 'J': /* jitter mode */ 858 if (error_code == 1) {
662 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode); 859 set_source_ip(config.source_ip, sockset.socket4, AF_INET);
663 if (!err) { 860 } else {
664 crash("Failed to parse jitter threshold"); 861 // just try this mindlessly if it's not a v4 address
665 } 862 set_source_ip(config.source_ip, sockset.socket6, AF_INET6);
863 }
864 }
666 865
667 jitter_mode = true; 866#ifdef SO_TIMESTAMP
668 break; 867 if (sockset.socket4 != -1) {
669 case 'M': /* MOS mode */ 868 int on = 1;
670 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode); 869 if (setsockopt(sockset.socket4, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
671 if (!err) { 870 if (debug) {
672 crash("Failed to parse MOS threshold"); 871 printf("Warning: no SO_TIMESTAMP support\n");
673 } 872 }
674 873 }
675 mos_mode = true; 874 }
676 break; 875 if (sockset.socket6 != -1) {
677 case 'S': /* score mode */ 876 int on = 1;
678 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode); 877 if (setsockopt(sockset.socket6, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
679 if (!err) { 878 if (debug) {
680 crash("Failed to parse score threshold"); 879 printf("Warning: no SO_TIMESTAMP support\n");
681 } 880 }
682
683 score_mode = true;
684 break;
685 case 'O': /* out of order mode */
686 order_mode = true;
687 break;
688 } 881 }
689 } 882 }
883#endif // SO_TIMESTAMP
690 } 884 }
691 885
692 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ 886 if (config.need_v6) {
693 environ = NULL; 887 sockset.socket6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
694 888 if (sockset.socket6 == -1) {
695 /* use the pid to mark packets as ours */ 889 crash("Failed to obtain ICMP v6 socket");
696 /* Some systems have 32-bit pid_t so mask off only 16 bits */
697 pid = getpid() & 0xffff;
698 /* printf("pid = %u\n", pid); */
699
700 /* Parse extra opts if any */
701 argv = np_extra_opts(&argc, argv, progname);
702
703 argv = &argv[optind];
704 while (*argv) {
705 add_target(*argv);
706 argv++;
707 }
708
709 if (!targets) {
710 errno = 0;
711 crash("No hosts to check");
712 }
713
714 // add_target might change address_family
715 switch (address_family) {
716 case AF_INET:
717 icmp_proto = IPPROTO_ICMP;
718 break;
719 case AF_INET6:
720 icmp_proto = IPPROTO_ICMPV6;
721 break;
722 default:
723 crash("Address family not supported");
724 }
725 if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1) {
726 sockets |= HAVE_ICMP;
727 } else {
728 icmp_sockerrno = errno;
729 }
730
731 if (source_ip) {
732 set_source_ip(source_ip);
733 }
734
735#ifdef SO_TIMESTAMP
736 if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
737 if (debug) {
738 printf("Warning: no SO_TIMESTAMP support\n");
739 } 890 }
740 } 891 }
741#endif // SO_TIMESTAMP
742 892
743 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ 893 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
744 if (setuid(getuid()) == -1) { 894 if (setuid(getuid()) == -1) {
@@ -746,186 +896,179 @@ int main(int argc, char **argv) {
746 return 1; 896 return 1;
747 } 897 }
748 898
749 if (!sockets) { 899 if (sockset.socket4) {
750 if (icmp_sock == -1) { 900 int result = setsockopt(sockset.socket4, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
751 errno = icmp_sockerrno;
752 crash("Failed to obtain ICMP socket");
753 return -1;
754 }
755 /* if(udp_sock == -1) { */
756 /* errno = icmp_sockerrno; */
757 /* crash("Failed to obtain UDP socket"); */
758 /* return -1; */
759 /* } */
760 /* if(tcp_sock == -1) { */
761 /* errno = icmp_sockerrno; */
762 /* crash("Failed to obtain TCP socker"); */
763 /* return -1; */
764 /* } */
765 }
766 if (!ttl) {
767 ttl = 64;
768 }
769
770 if (icmp_sock) {
771 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
772 if (debug) { 901 if (debug) {
773 if (result == -1) { 902 if (result == -1) {
774 printf("setsockopt failed\n"); 903 printf("setsockopt failed\n");
775 } else { 904 } else {
776 printf("ttl set to %u\n", ttl); 905 printf("ttl set to %lu\n", config.ttl);
777 } 906 }
778 } 907 }
779 } 908 }
780 909
781 /* stupid users should be able to give whatever thresholds they want 910 if (sockset.socket6) {
782 * (nothing will break if they do), but some anal plugin maintainer 911 int result = setsockopt(sockset.socket6, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
783 * will probably add some printf() thing here later, so it might be 912 if (debug) {
784 * best to at least show them where to do it. ;) */ 913 if (result == -1) {
785 if (warn.pl > crit.pl) { 914 printf("setsockopt failed\n");
786 warn.pl = crit.pl; 915 } else {
787 } 916 printf("ttl set to %lu\n", config.ttl);
788 if (warn.rta > crit.rta) { 917 }
789 warn.rta = crit.rta; 918 }
790 }
791 if (warn_down > crit_down) {
792 crit_down = warn_down;
793 }
794 if (warn.jitter > crit.jitter) {
795 crit.jitter = warn.jitter;
796 }
797 if (warn.mos < crit.mos) {
798 warn.mos = crit.mos;
799 }
800 if (warn.score < crit.score) {
801 warn.score = crit.score;
802 }
803
804#ifdef HAVE_SIGACTION
805 sig_action.sa_sigaction = NULL;
806 sig_action.sa_handler = finish;
807 sigfillset(&sig_action.sa_mask);
808 sig_action.sa_flags = SA_NODEFER | SA_RESTART;
809 sigaction(SIGINT, &sig_action, NULL);
810 sigaction(SIGHUP, &sig_action, NULL);
811 sigaction(SIGTERM, &sig_action, NULL);
812 sigaction(SIGALRM, &sig_action, NULL);
813#else /* HAVE_SIGACTION */
814 signal(SIGINT, finish);
815 signal(SIGHUP, finish);
816 signal(SIGTERM, finish);
817 signal(SIGALRM, finish);
818#endif /* HAVE_SIGACTION */
819 if (debug) {
820 printf("Setting alarm timeout to %u seconds\n", timeout);
821 } 919 }
822 alarm(timeout);
823 920
824 /* make sure we don't wait any longer than necessary */ 921 /* make sure we don't wait any longer than necessary */
825 gettimeofday(&prog_start, &tz); 922 struct timeval prog_start;
826 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta; 923 gettimeofday(&prog_start, NULL);
924
925 time_t max_completion_time =
926 (config.target_interval * config.number_of_targets) +
927 (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta;
827 928
828 if (debug) { 929 if (debug) {
829 printf("packets: %u, targets: %u\n" 930 printf("packets: %u, targets: %u\n"
830 "target_interval: %0.3f, pkt_interval %0.3f\n" 931 "target_interval: %0.3f\n"
831 "crit.rta: %0.3f\n" 932 "crit.rta: %0.3f\n"
832 "max_completion_time: %0.3f\n", 933 "max_completion_time: %0.3f\n",
833 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, 934 config.number_of_packets, config.number_of_targets,
935 (float)config.target_interval / 1000, (float)config.crit.rta / 1000,
834 (float)max_completion_time / 1000); 936 (float)max_completion_time / 1000);
835 } 937 }
836 938
837 if (debug) { 939 if (debug) {
838 if (max_completion_time > (u_int)timeout * 1000000) { 940 if (max_completion_time > (timeout * 1000000)) {
839 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); 941 printf("max_completion_time: %ld timeout: %u\n", max_completion_time, timeout);
840 printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1); 942 printf("Timeout must be at least %ld\n", (max_completion_time / 1000000) + 1);
841 } 943 }
842 } 944 }
843 945
844 if (debug) { 946 if (debug) {
845 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); 947 printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl,
846 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval); 948 config.warn.rta, config.warn.pl);
847 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout); 949 printf("target_interval: %ld\n", config.target_interval);
950 printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_data_size + ICMP_MINLEN, timeout);
848 } 951 }
849 952
850 if (packets > 20) { 953 if (config.min_hosts_alive < -1) {
851 errno = 0; 954 errno = 0;
852 crash("packets is > 20 (%d)", packets); 955 crash("minimum alive hosts is negative (%i)", config.min_hosts_alive);
853 } 956 }
854 957
855 if (min_hosts_alive < -1) { 958 // Build an index table of all targets
856 errno = 0; 959 ping_target *host = config.targets;
857 crash("minimum alive hosts is negative (%i)", min_hosts_alive); 960 ping_target **table = malloc(sizeof(ping_target *) * config.number_of_targets);
858 }
859
860 host = list;
861 table = malloc(sizeof(struct rta_host *) * targets);
862 if (!table) { 961 if (!table) {
863 crash("main(): malloc failed for host table"); 962 crash("main(): malloc failed for host table");
864 } 963 }
865 964
866 i = 0; 965 unsigned short target_index = 0;
867 while (host) { 966 while (host) {
868 host->id = i * packets; 967 host->id = target_index * config.number_of_packets;
869 table[i] = host; 968 table[target_index] = host;
870 host = host->next; 969 host = host->next;
871 i++; 970 target_index++;
872 } 971 }
873 972
874 run_checks(); 973 time_t target_interval = config.target_interval;
974
975 check_icmp_state program_state = check_icmp_state_init();
976
977 run_checks(config.icmp_data_size, &target_interval, config.sender_id, config.mode,
978 max_completion_time, prog_start, table, config.number_of_packets, sockset,
979 config.number_of_targets, &program_state);
875 980
876 errno = 0; 981 errno = 0;
877 finish(0);
878 982
879 return (0); 983 mp_check overall = mp_check_init();
880} 984 finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit,
985 config.number_of_targets, &program_state, config.hosts, config.number_of_hosts,
986 &overall);
881 987
882static void run_checks(void) { 988 if (sockset.socket4) {
883 u_int i, t; 989 close(sockset.socket4);
884 u_int final_wait, time_passed; 990 }
991 if (sockset.socket6) {
992 close(sockset.socket6);
993 }
994
995 mp_exit(overall);
996}
885 997
998static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval,
999 const uint16_t sender_id, const check_icmp_execution_mode mode,
1000 const time_t max_completion_time, const struct timeval prog_start,
1001 ping_target **table, const unsigned short packets,
1002 const check_icmp_socket_set sockset, const unsigned short number_of_targets,
1003 check_icmp_state *program_state) {
886 /* this loop might actually violate the pkt_interval or target_interval 1004 /* this loop might actually violate the pkt_interval or target_interval
887 * settings, but only if there aren't any packets on the wire which 1005 * settings, but only if there aren't any packets on the wire which
888 * indicates that the target can handle an increased packet rate */ 1006 * indicates that the target can handle an increased packet rate */
889 for (i = 0; i < packets; i++) { 1007 for (unsigned int packet_index = 0; packet_index < packets; packet_index++) {
890 for (t = 0; t < targets; t++) { 1008 for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) {
891 /* don't send useless packets */ 1009 /* don't send useless packets */
892 if (!targets_alive) { 1010 if (!targets_alive(number_of_targets, program_state->targets_down)) {
893 finish(0); 1011 return;
894 } 1012 }
895 if (table[t]->flags & FLAG_LOST_CAUSE) { 1013 if (table[target_index]->flags & FLAG_LOST_CAUSE) {
896 if (debug) { 1014 if (debug) {
897 printf("%s is a lost cause. not sending any more\n", table[t]->name); 1015
1016 char address[INET6_ADDRSTRLEN];
1017 parse_address(&table[target_index]->address, address, sizeof(address));
1018 printf("%s is a lost cause. not sending any more\n", address);
898 } 1019 }
899 continue; 1020 continue;
900 } 1021 }
901 1022
902 /* we're still in the game, so send next packet */ 1023 /* we're still in the game, so send next packet */
903 (void)send_icmp_ping(icmp_sock, table[t]); 1024 (void)send_icmp_ping(sockset, table[target_index], icmp_pkt_size, sender_id,
904 wait_for_reply(icmp_sock, target_interval); 1025 program_state);
1026
1027 /* wrap up if all targets are declared dead */
1028 if (targets_alive(number_of_targets, program_state->targets_down) ||
1029 get_timevaldiff(prog_start, prog_start) < max_completion_time ||
1030 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1031 wait_for_reply(sockset, *target_interval, icmp_pkt_size, target_interval, sender_id,
1032 table, packets, number_of_targets, program_state);
1033 }
1034 }
1035 if (targets_alive(number_of_targets, program_state->targets_down) ||
1036 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1037 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1038 wait_for_reply(sockset, number_of_targets, icmp_pkt_size, target_interval, sender_id,
1039 table, packets, number_of_targets, program_state);
905 } 1040 }
906 wait_for_reply(icmp_sock, pkt_interval * targets);
907 } 1041 }
908 1042
909 if (icmp_pkts_en_route && targets_alive) { 1043 if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
910 time_passed = get_timevaldiff(NULL, NULL); 1044 program_state->icmp_lost) &&
911 final_wait = max_completion_time - time_passed; 1045 targets_alive(number_of_targets, program_state->targets_down)) {
1046 time_t time_passed = get_timevaldiff_to_now(prog_start);
1047 time_t final_wait = max_completion_time - time_passed;
912 1048
913 if (debug) { 1049 if (debug) {
914 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); 1050 printf("time_passed: %ld final_wait: %ld max_completion_time: %ld\n", time_passed,
1051 final_wait, max_completion_time);
915 } 1052 }
916 if (time_passed > max_completion_time) { 1053 if (time_passed > max_completion_time) {
917 if (debug) { 1054 if (debug) {
918 printf("Time passed. Finishing up\n"); 1055 printf("Time passed. Finishing up\n");
919 } 1056 }
920 finish(0); 1057 return;
921 } 1058 }
922 1059
923 /* catch the packets that might come in within the timeframe, but 1060 /* catch the packets that might come in within the timeframe, but
924 * haven't yet */ 1061 * haven't yet */
925 if (debug) { 1062 if (debug) {
926 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); 1063 printf("Waiting for %ld micro-seconds (%0.3f msecs)\n", final_wait,
1064 (float)final_wait / 1000);
1065 }
1066 if (targets_alive(number_of_targets, program_state->targets_down) ||
1067 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1068 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1069 wait_for_reply(sockset, final_wait, icmp_pkt_size, target_interval, sender_id, table,
1070 packets, number_of_targets, program_state);
927 } 1071 }
928 wait_for_reply(icmp_sock, final_wait);
929 } 1072 }
930} 1073}
931 1074
@@ -939,18 +1082,11 @@ static void run_checks(void) {
939 * both: 1082 * both:
940 * icmp echo reply : the rest 1083 * icmp echo reply : the rest
941 */ 1084 */
942static int wait_for_reply(int sock, u_int t) { 1085static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval,
943 int n, hlen; 1086 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
944 static unsigned char buf[65536]; 1087 ping_target **table, const unsigned short packets,
945 struct sockaddr_storage resp_addr; 1088 const unsigned short number_of_targets, check_icmp_state *program_state) {
946 union ip_hdr *ip;
947 union icmp_packet packet; 1089 union icmp_packet packet;
948 struct rta_host *host;
949 struct icmp_ping_data data;
950 struct timeval wait_start, now;
951 u_int tdiff, i, per_pkt_wait;
952 double jitter_tmp;
953
954 if (!(packet.buf = malloc(icmp_pkt_size))) { 1090 if (!(packet.buf = malloc(icmp_pkt_size))) {
955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1091 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
956 return -1; /* might be reached if we're in debug mode */ 1092 return -1; /* might be reached if we're in debug mode */
@@ -959,177 +1095,174 @@ static int wait_for_reply(int sock, u_int t) {
959 memset(packet.buf, 0, icmp_pkt_size); 1095 memset(packet.buf, 0, icmp_pkt_size);
960 1096
961 /* if we can't listen or don't have anything to listen to, just return */ 1097 /* if we can't listen or don't have anything to listen to, just return */
962 if (!t || !icmp_pkts_en_route) { 1098 if (!time_interval || !icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
1099 program_state->icmp_lost)) {
963 free(packet.buf); 1100 free(packet.buf);
964 return 0; 1101 return 0;
965 } 1102 }
966 1103
967 gettimeofday(&wait_start, &tz); 1104 // Get current time stamp
1105 struct timeval wait_start;
1106 gettimeofday(&wait_start, NULL);
968 1107
969 i = t; 1108 struct sockaddr_storage resp_addr;
970 per_pkt_wait = t / icmp_pkts_en_route; 1109 time_t per_pkt_wait =
971 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) { 1110 time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
972 t = per_pkt_wait; 1111 program_state->icmp_lost);
973 1112 static unsigned char buf[65536];
974 /* wrap up if all targets are declared dead */ 1113 union ip_hdr *ip_header;
975 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) { 1114 struct timeval packet_received_timestamp;
976 finish(0); 1115 while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
977 } 1116 program_state->icmp_lost) &&
1117 get_timevaldiff_to_now(wait_start) < time_interval) {
1118 time_t loop_time_interval = per_pkt_wait;
978 1119
979 /* reap responses until we hit a timeout */ 1120 /* reap responses until we hit a timeout */
980 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); 1121 recvfrom_wto_wrapper recv_foo =
981 if (!n) { 1122 recvfrom_wto(sockset, buf, sizeof(buf), (struct sockaddr *)&resp_addr,
1123 &loop_time_interval, &packet_received_timestamp);
1124 if (!recv_foo.received) {
982 if (debug > 1) { 1125 if (debug > 1) {
983 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); 1126 printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait);
984 } 1127 }
985 continue; /* timeout for this one, so keep trying */ 1128 continue; /* timeout for this one, so keep trying */
986 } 1129 }
987 if (n < 0) { 1130
1131 if (recv_foo.received < 0) {
988 if (debug) { 1132 if (debug) {
989 printf("recvfrom_wto() returned errors\n"); 1133 printf("recvfrom_wto() returned errors\n");
990 } 1134 }
991 free(packet.buf); 1135 free(packet.buf);
992 return n; 1136 return (int)recv_foo.received;
993 } 1137 }
994 1138
995 // FIXME: with ipv6 we don't have an ip header here 1139 if (recv_foo.recv_proto != AF_INET6) {
996 if (address_family != AF_INET6) { 1140 ip_header = (union ip_hdr *)buf;
997 ip = (union ip_hdr *)buf;
998 1141
999 if (debug > 1) { 1142 if (debug > 1) {
1000 char address[INET6_ADDRSTRLEN]; 1143 char address[INET6_ADDRSTRLEN];
1001 parse_address(&resp_addr, address, sizeof(address)); 1144 parse_address(&resp_addr, address, sizeof(address));
1002 printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address); 1145 printf("received %u bytes from %s\n",
1146 address_family == AF_INET6 ? ntohs(ip_header->ip6.ip6_plen)
1147 : ntohs(ip_header->ip.ip_len),
1148 address);
1003 } 1149 }
1004 } 1150 }
1005 1151
1006 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 1152 int hlen = (recv_foo.recv_proto == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2;
1007 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 1153
1008 /* alpha headers are decidedly broken. Using an ansi compiler, 1154 if (recv_foo.received < (hlen + ICMP_MINLEN)) {
1009 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
1010 * off the bottom 4 bits */
1011 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
1012 /* #else */
1013 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
1014 /* #endif */
1015
1016 if (n < (hlen + ICMP_MINLEN)) {
1017 char address[INET6_ADDRSTRLEN]; 1155 char address[INET6_ADDRSTRLEN];
1018 parse_address(&resp_addr, address, sizeof(address)); 1156 parse_address(&resp_addr, address, sizeof(address));
1019 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address); 1157 crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n",
1158 recv_foo.received, hlen + icmp_pkt_size, address);
1020 } 1159 }
1021 /* else if(debug) { */
1022 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
1023 /* hlen, ntohs(ip->ip_len) - hlen, */
1024 /* sizeof(struct ip), icmp_pkt_size); */
1025 /* } */
1026
1027 /* check the response */ 1160 /* check the response */
1028
1029 memcpy(packet.buf, buf + hlen, icmp_pkt_size); 1161 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
1030 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
1031 : sizeof(struct icmp));*/
1032 1162
1033 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || 1163 if ((recv_foo.recv_proto == AF_INET &&
1034 ntohs(packet.icp->icmp_seq) >= targets * packets)) || 1164 (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY ||
1035 (address_family == PF_INET6 && (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || 1165 ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) ||
1036 ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { 1166 (recv_foo.recv_proto == AF_INET6 &&
1167 (ntohs(packet.icp6->icmp6_id) != sender_id ||
1168 packet.icp6->icmp6_type != ICMP6_ECHO_REPLY ||
1169 ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) {
1037 if (debug > 2) { 1170 if (debug > 2) {
1038 printf("not a proper ICMP_ECHOREPLY\n"); 1171 printf("not a proper ICMP_ECHOREPLY\n");
1039 } 1172 }
1040 handle_random_icmp(buf + hlen, &resp_addr); 1173
1174 handle_random_icmp(buf + hlen, &resp_addr, target_interval, sender_id, table, packets,
1175 number_of_targets, program_state);
1176
1041 continue; 1177 continue;
1042 } 1178 }
1043 1179
1044 /* this is indeed a valid response */ 1180 /* this is indeed a valid response */
1045 if (address_family == PF_INET) { 1181 ping_target *target;
1182 struct icmp_ping_data data;
1183 if (address_family == AF_INET) {
1046 memcpy(&data, packet.icp->icmp_data, sizeof(data)); 1184 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1047 if (debug > 2) { 1185 if (debug > 2) {
1048 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 1186 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1049 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); 1187 ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq),
1188 packet.icp->icmp_cksum);
1050 } 1189 }
1051 host = table[ntohs(packet.icp->icmp_seq) / packets]; 1190 target = table[ntohs(packet.icp->icmp_seq) / packets];
1052 } else { 1191 } else {
1053 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); 1192 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1054 if (debug > 2) { 1193 if (debug > 2) {
1055 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), 1194 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1056 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); 1195 ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq),
1196 packet.icp6->icmp6_cksum);
1057 } 1197 }
1058 host = table[ntohs(packet.icp6->icmp6_seq) / packets]; 1198 target = table[ntohs(packet.icp6->icmp6_seq) / packets];
1059 } 1199 }
1060 1200
1061 tdiff = get_timevaldiff(&data.stime, &now); 1201 time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp);
1062 1202
1063 if (host->last_tdiff > 0) { 1203 if (target->last_tdiff > 0) {
1064 /* Calculate jitter */ 1204 /* Calculate jitter */
1065 if (host->last_tdiff > tdiff) { 1205 double jitter_tmp;
1066 jitter_tmp = host->last_tdiff - tdiff; 1206 if (target->last_tdiff > tdiff) {
1207 jitter_tmp = (double)(target->last_tdiff - tdiff);
1067 } else { 1208 } else {
1068 jitter_tmp = tdiff - host->last_tdiff; 1209 jitter_tmp = (double)(tdiff - target->last_tdiff);
1069 } 1210 }
1070 1211
1071 if (host->jitter == 0) { 1212 if (target->jitter == 0) {
1072 host->jitter = jitter_tmp; 1213 target->jitter = jitter_tmp;
1073 host->jitter_max = jitter_tmp; 1214 target->jitter_max = jitter_tmp;
1074 host->jitter_min = jitter_tmp; 1215 target->jitter_min = jitter_tmp;
1075 } else { 1216 } else {
1076 host->jitter += jitter_tmp; 1217 target->jitter += jitter_tmp;
1077 1218
1078 if (jitter_tmp < host->jitter_min) { 1219 if (jitter_tmp < target->jitter_min) {
1079 host->jitter_min = jitter_tmp; 1220 target->jitter_min = jitter_tmp;
1080 } 1221 }
1081 1222
1082 if (jitter_tmp > host->jitter_max) { 1223 if (jitter_tmp > target->jitter_max) {
1083 host->jitter_max = jitter_tmp; 1224 target->jitter_max = jitter_tmp;
1084 } 1225 }
1085 } 1226 }
1086 1227
1087 /* Check if packets in order */ 1228 /* Check if packets in order */
1088 if (host->last_icmp_seq >= packet.icp->icmp_seq) { 1229 if (target->last_icmp_seq >= packet.icp->icmp_seq) {
1089 host->order_status = STATE_CRITICAL; 1230 target->found_out_of_order_packets = true;
1090 } 1231 }
1091 } 1232 }
1092 host->last_tdiff = tdiff; 1233 target->last_tdiff = tdiff;
1093 1234
1094 host->last_icmp_seq = packet.icp->icmp_seq; 1235 target->last_icmp_seq = packet.icp->icmp_seq;
1095 1236
1096 host->time_waited += tdiff; 1237 target->time_waited += tdiff;
1097 host->icmp_recv++; 1238 target->icmp_recv++;
1098 icmp_recv++; 1239 program_state->icmp_recv++;
1099 1240
1100 if (tdiff > (unsigned int)host->rtmax) { 1241 if (tdiff > (unsigned int)target->rtmax) {
1101 host->rtmax = tdiff; 1242 target->rtmax = (double)tdiff;
1102 } 1243 }
1103 1244
1104 if ((host->rtmin == INFINITY) || (tdiff < (unsigned int)host->rtmin)) { 1245 if ((target->rtmin == INFINITY) || (tdiff < (unsigned int)target->rtmin)) {
1105 host->rtmin = tdiff; 1246 target->rtmin = (double)tdiff;
1106 } 1247 }
1107 1248
1108 if (debug) { 1249 if (debug) {
1109 char address[INET6_ADDRSTRLEN]; 1250 char address[INET6_ADDRSTRLEN];
1110 parse_address(&resp_addr, address, sizeof(address)); 1251 parse_address(&resp_addr, address, sizeof(address));
1111 1252
1112 switch (address_family) { 1253 switch (recv_foo.recv_proto) {
1113 case AF_INET: { 1254 case AF_INET: {
1114 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, 1255 printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
1115 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1256 (float)tdiff / 1000, address, ip_header->ip.ip_ttl,
1257 (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1116 break; 1258 break;
1117 }; 1259 };
1118 case AF_INET6: { 1260 case AF_INET6: {
1119 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, 1261 printf("%0.3f ms rtt from %s, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000,
1120 (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1262 address, (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1121 }; 1263 };
1122 } 1264 }
1123 } 1265 }
1124
1125 /* if we're in hostcheck mode, exit with limited printouts */
1126 if (mode == MODE_HOSTCHECK) {
1127 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1128 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1129 host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000,
1130 (float)crit.rta / 1000);
1131 exit(STATE_OK);
1132 }
1133 } 1266 }
1134 1267
1135 free(packet.buf); 1268 free(packet.buf);
@@ -1137,38 +1270,28 @@ static int wait_for_reply(int sock, u_int t) {
1137} 1270}
1138 1271
1139/* the ping functions */ 1272/* the ping functions */
1140static int send_icmp_ping(int sock, struct rta_host *host) { 1273static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host,
1141 long int len; 1274 const unsigned short icmp_pkt_size, const uint16_t sender_id,
1142 size_t addrlen; 1275 check_icmp_state *program_state) {
1143 struct icmp_ping_data data; 1276 void *buf = calloc(1, icmp_pkt_size);
1144 struct msghdr hdr;
1145 struct iovec iov;
1146 struct timeval tv;
1147 void *buf = NULL;
1148
1149 if (sock == -1) {
1150 errno = 0;
1151 crash("Attempt to send on bogus socket");
1152 return -1;
1153 }
1154
1155 if (!buf) { 1277 if (!buf) {
1156 if (!(buf = malloc(icmp_pkt_size))) { 1278 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
1157 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1279 return -1; /* might be reached if we're in debug mode */
1158 return -1; /* might be reached if we're in debug mode */
1159 }
1160 } 1280 }
1161 memset(buf, 0, icmp_pkt_size);
1162 1281
1163 if ((gettimeofday(&tv, &tz)) == -1) { 1282 struct timeval current_time;
1283 if ((gettimeofday(&current_time, NULL)) == -1) {
1164 free(buf); 1284 free(buf);
1165 return -1; 1285 return -1;
1166 } 1286 }
1167 1287
1288 struct icmp_ping_data data;
1168 data.ping_id = 10; /* host->icmp.icmp_sent; */ 1289 data.ping_id = 10; /* host->icmp.icmp_sent; */
1169 memcpy(&data.stime, &tv, sizeof(tv)); 1290 memcpy(&data.stime, &current_time, sizeof(current_time));
1170 1291
1171 if (address_family == AF_INET) { 1292 socklen_t addrlen = 0;
1293
1294 if (host->address.ss_family == AF_INET) {
1172 struct icmp *icp = (struct icmp *)buf; 1295 struct icmp *icp = (struct icmp *)buf;
1173 addrlen = sizeof(struct sockaddr_in); 1296 addrlen = sizeof(struct sockaddr_in);
1174 1297
@@ -1177,15 +1300,19 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1177 icp->icmp_type = ICMP_ECHO; 1300 icp->icmp_type = ICMP_ECHO;
1178 icp->icmp_code = 0; 1301 icp->icmp_code = 0;
1179 icp->icmp_cksum = 0; 1302 icp->icmp_cksum = 0;
1180 icp->icmp_id = htons(pid); 1303 icp->icmp_id = htons((uint16_t)sender_id);
1181 icp->icmp_seq = htons(host->id++); 1304 icp->icmp_seq = htons(host->id++);
1182 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); 1305 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1183 1306
1184 if (debug > 2) { 1307 if (debug > 2) {
1185 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1308 char address[INET6_ADDRSTRLEN];
1186 ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); 1309 parse_address((&host->address), address, sizeof(address));
1310
1311 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1312 sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum,
1313 address);
1187 } 1314 }
1188 } else { 1315 } else if (host->address.ss_family == AF_INET6) {
1189 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; 1316 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1190 addrlen = sizeof(struct sockaddr_in6); 1317 addrlen = sizeof(struct sockaddr_in6);
1191 1318
@@ -1194,659 +1321,431 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1194 icp6->icmp6_type = ICMP6_ECHO_REQUEST; 1321 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1195 icp6->icmp6_code = 0; 1322 icp6->icmp6_code = 0;
1196 icp6->icmp6_cksum = 0; 1323 icp6->icmp6_cksum = 0;
1197 icp6->icmp6_id = htons(pid); 1324 icp6->icmp6_id = htons((uint16_t)sender_id);
1198 icp6->icmp6_seq = htons(host->id++); 1325 icp6->icmp6_seq = htons(host->id++);
1199 // let checksum be calculated automatically 1326 // let checksum be calculated automatically
1200 1327
1201 if (debug > 2) { 1328 if (debug > 2) {
1202 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1329 char address[INET6_ADDRSTRLEN];
1203 ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); 1330 parse_address((&host->address), address, sizeof(address));
1331
1332 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to target %s\n",
1333 sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum,
1334 address);
1204 } 1335 }
1336 } else {
1337 // unknown address family
1338 crash("unknown address family in %s", __func__);
1205 } 1339 }
1206 1340
1341 struct iovec iov;
1207 memset(&iov, 0, sizeof(iov)); 1342 memset(&iov, 0, sizeof(iov));
1208 iov.iov_base = buf; 1343 iov.iov_base = buf;
1209 iov.iov_len = icmp_pkt_size; 1344 iov.iov_len = icmp_pkt_size;
1210 1345
1346 struct msghdr hdr;
1211 memset(&hdr, 0, sizeof(hdr)); 1347 memset(&hdr, 0, sizeof(hdr));
1212 hdr.msg_name = (struct sockaddr *)&host->saddr_in; 1348 hdr.msg_name = (struct sockaddr *)&host->address;
1213 hdr.msg_namelen = addrlen; 1349 hdr.msg_namelen = addrlen;
1214 hdr.msg_iov = &iov; 1350 hdr.msg_iov = &iov;
1215 hdr.msg_iovlen = 1; 1351 hdr.msg_iovlen = 1;
1216 1352
1217 errno = 0; 1353 errno = 0;
1218 1354
1219/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1355 long int len;
1356 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1357 if (host->address.ss_family == AF_INET) {
1358#ifdef MSG_CONFIRM
1359 len = sendmsg(sockset.socket4, &hdr, MSG_CONFIRM);
1360#else
1361 len = sendmsg(sockset.socket4, &hdr, 0);
1362#endif
1363 } else if (host->address.ss_family == AF_INET6) {
1220#ifdef MSG_CONFIRM 1364#ifdef MSG_CONFIRM
1221 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1365 len = sendmsg(sockset.socket6, &hdr, MSG_CONFIRM);
1222#else 1366#else
1223 len = sendmsg(sock, &hdr, 0); 1367 len = sendmsg(sockset.socket6, &hdr, 0);
1224#endif 1368#endif
1369 } else {
1370 assert(false);
1371 }
1225 1372
1226 free(buf); 1373 free(buf);
1227 1374
1228 if (len < 0 || (unsigned int)len != icmp_pkt_size) { 1375 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1229 if (debug) { 1376 if (debug) {
1230 char address[INET6_ADDRSTRLEN]; 1377 char address[INET6_ADDRSTRLEN];
1231 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); 1378 parse_address((&host->address), address, sizeof(address));
1232 printf("Failed to send ping to %s: %s\n", address, strerror(errno)); 1379 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1233 } 1380 }
1234 errno = 0; 1381 errno = 0;
1235 return -1; 1382 return -1;
1236 } 1383 }
1237 1384
1238 icmp_sent++; 1385 program_state->icmp_sent++;
1239 host->icmp_sent++; 1386 host->icmp_sent++;
1240 1387
1241 return 0; 1388 return 0;
1242} 1389}
1243 1390
1244static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { 1391static recvfrom_wto_wrapper recvfrom_wto(const check_icmp_socket_set sockset, void *buf,
1245 u_int slen; 1392 const unsigned int len, struct sockaddr *saddr,
1246 int n, ret; 1393 time_t *timeout, struct timeval *received_timestamp) {
1247 struct timeval to, then, now;
1248 fd_set rd, wr;
1249#ifdef HAVE_MSGHDR_MSG_CONTROL 1394#ifdef HAVE_MSGHDR_MSG_CONTROL
1250 char ans_data[4096]; 1395 char ans_data[4096];
1251#endif // HAVE_MSGHDR_MSG_CONTROL 1396#endif // HAVE_MSGHDR_MSG_CONTROL
1252 struct msghdr hdr;
1253 struct iovec iov;
1254#ifdef SO_TIMESTAMP 1397#ifdef SO_TIMESTAMP
1255 struct cmsghdr *chdr; 1398 struct cmsghdr *chdr;
1256#endif 1399#endif
1257 1400
1258 if (!*timo) { 1401 recvfrom_wto_wrapper result = {
1402 .received = 0,
1403 .recv_proto = AF_UNSPEC,
1404 };
1405
1406 if (!*timeout) {
1259 if (debug) { 1407 if (debug) {
1260 printf("*timo is not\n"); 1408 printf("*timeout is not\n");
1261 } 1409 }
1262 return 0; 1410 return result;
1411 }
1412
1413 struct timeval real_timeout;
1414 real_timeout.tv_sec = *timeout / 1000000;
1415 real_timeout.tv_usec = (*timeout - (real_timeout.tv_sec * 1000000));
1416
1417 // Dummy fds for select
1418 fd_set dummy_write_fds;
1419 FD_ZERO(&dummy_write_fds);
1420
1421 // Read fds for select with the socket
1422 fd_set read_fds;
1423 FD_ZERO(&read_fds);
1424
1425 if (sockset.socket4 != -1) {
1426 FD_SET(sockset.socket4, &read_fds);
1427 }
1428 if (sockset.socket6 != -1) {
1429 FD_SET(sockset.socket6, &read_fds);
1263 } 1430 }
1264 1431
1265 to.tv_sec = *timo / 1000000; 1432 int nfds = (sockset.socket4 > sockset.socket6 ? sockset.socket4 : sockset.socket6) + 1;
1266 to.tv_usec = (*timo - (to.tv_sec * 1000000)); 1433
1434 struct timeval then;
1435 gettimeofday(&then, NULL);
1267 1436
1268 FD_ZERO(&rd);
1269 FD_ZERO(&wr);
1270 FD_SET(sock, &rd);
1271 errno = 0; 1437 errno = 0;
1272 gettimeofday(&then, &tz); 1438 int select_return = select(nfds, &read_fds, &dummy_write_fds, NULL, &real_timeout);
1273 n = select(sock + 1, &rd, &wr, NULL, &to); 1439 if (select_return < 0) {
1274 if (n < 0) {
1275 crash("select() in recvfrom_wto"); 1440 crash("select() in recvfrom_wto");
1276 } 1441 }
1277 gettimeofday(&now, &tz);
1278 *timo = get_timevaldiff(&then, &now);
1279 1442
1280 if (!n) { 1443 struct timeval now;
1281 return 0; /* timeout */ 1444 gettimeofday(&now, NULL);
1445 *timeout = get_timevaldiff(then, now);
1446
1447 if (!select_return) {
1448 return result; /* timeout */
1282 } 1449 }
1283 1450
1284 slen = sizeof(struct sockaddr_storage); 1451 unsigned int slen = sizeof(struct sockaddr_storage);
1285 1452
1286 memset(&iov, 0, sizeof(iov)); 1453 struct iovec iov = {
1287 iov.iov_base = buf; 1454 .iov_base = buf,
1288 iov.iov_len = len; 1455 .iov_len = len,
1456 };
1289 1457
1290 memset(&hdr, 0, sizeof(hdr)); 1458 struct msghdr hdr = {
1291 hdr.msg_name = saddr; 1459 .msg_name = saddr,
1292 hdr.msg_namelen = slen; 1460 .msg_namelen = slen,
1293 hdr.msg_iov = &iov; 1461 .msg_iov = &iov,
1294 hdr.msg_iovlen = 1; 1462 .msg_iovlen = 1,
1295#ifdef HAVE_MSGHDR_MSG_CONTROL 1463#ifdef HAVE_MSGHDR_MSG_CONTROL
1296 hdr.msg_control = ans_data; 1464 .msg_control = ans_data,
1297 hdr.msg_controllen = sizeof(ans_data); 1465 .msg_controllen = sizeof(ans_data),
1298#endif 1466#endif
1467 };
1468
1469 ssize_t ret;
1470 if (FD_ISSET(sockset.socket4, &read_fds)) {
1471 ret = recvmsg(sockset.socket4, &hdr, 0);
1472 result.recv_proto = AF_INET;
1473 } else if (FD_ISSET(sockset.socket6, &read_fds)) {
1474 ret = recvmsg(sockset.socket6, &hdr, 0);
1475 result.recv_proto = AF_INET6;
1476 } else {
1477 assert(false);
1478 }
1479
1480 result.received = ret;
1299 1481
1300 ret = recvmsg(sock, &hdr, 0);
1301#ifdef SO_TIMESTAMP 1482#ifdef SO_TIMESTAMP
1302 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { 1483 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1303 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { 1484 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP &&
1304 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); 1485 chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1486 memcpy(received_timestamp, CMSG_DATA(chdr), sizeof(*received_timestamp));
1305 break; 1487 break;
1306 } 1488 }
1307 } 1489 }
1308 1490
1309 if (!chdr) 1491 if (!chdr) {
1492 gettimeofday(received_timestamp, NULL);
1493 }
1494#else
1495 gettimeofday(tv, NULL);
1310#endif // SO_TIMESTAMP 1496#endif // SO_TIMESTAMP
1311 gettimeofday(tv, &tz);
1312 return (ret);
1313}
1314 1497
1315static void finish(int sig) { 1498 return (result);
1316 u_int i = 0; 1499}
1317 unsigned char pl;
1318 double rta;
1319 struct rta_host *host;
1320 const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1321 int hosts_ok = 0;
1322 int hosts_warn = 0;
1323 int this_status;
1324 double R;
1325 1500
1501static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive,
1502 check_icmp_threshold warn, check_icmp_threshold crit,
1503 const unsigned short number_of_targets, check_icmp_state *program_state,
1504 check_icmp_target_container host_list[], unsigned short number_of_hosts,
1505 mp_check overall[static 1]) {
1506 // Deactivate alarm
1326 alarm(0); 1507 alarm(0);
1508
1327 if (debug > 1) { 1509 if (debug > 1) {
1328 printf("finish(%d) called\n", sig); 1510 printf("finish(%d) called\n", sig);
1329 } 1511 }
1330 1512
1331 if (icmp_sock != -1) {
1332 close(icmp_sock);
1333 }
1334 if (udp_sock != -1) {
1335 close(udp_sock);
1336 }
1337 if (tcp_sock != -1) {
1338 close(tcp_sock);
1339 }
1340
1341 if (debug) { 1513 if (debug) {
1342 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost); 1514 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", program_state->icmp_sent,
1343 printf("targets: %u targets_alive: %u\n", targets, targets_alive); 1515 program_state->icmp_recv, program_state->icmp_lost);
1516 printf("targets: %u targets_alive: %u\n", number_of_targets,
1517 targets_alive(number_of_targets, program_state->targets_down));
1344 } 1518 }
1345 1519
1346 /* iterate thrice to calculate values, give output, and print perfparse */ 1520 // loop over targets to evaluate each one
1347 status = STATE_OK; 1521 int targets_ok = 0;
1348 host = list; 1522 int targets_warn = 0;
1349 1523 for (unsigned short i = 0; i < number_of_hosts; i++) {
1350 while (host) { 1524 evaluate_host_wrapper host_check = evaluate_host(host_list[i], modes, warn, crit);
1351 this_status = STATE_OK;
1352
1353 if (!host->icmp_recv) {
1354 /* rta 0 is ofcourse not entirely correct, but will still show up
1355 * conspicuously as missing entries in perfparse and cacti */
1356 pl = 100;
1357 rta = 0;
1358 status = STATE_CRITICAL;
1359 /* up the down counter if not already counted */
1360 if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive) {
1361 targets_down++;
1362 }
1363 } else {
1364 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1365 rta = (double)host->time_waited / host->icmp_recv;
1366 }
1367
1368 if (host->icmp_recv > 1) {
1369 /*
1370 * This algorithm is probably pretty much blindly copied from
1371 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1372 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1373 * According to some quick research MOS originates from the Audio/Video transport network area.
1374 * Whether it can and should be computed from ICMP data, I can not say.
1375 *
1376 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1377 *
1378 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1379 *
1380 * More links:
1381 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1382 */
1383 host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000);
1384
1385 /*
1386 * Take the average round trip latency (in milliseconds), add
1387 * round trip jitter, but double the impact to latency
1388 * then add 10 for protocol latencies (in milliseconds).
1389 */
1390 host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10;
1391
1392 if (host->EffectiveLatency < 160) {
1393 R = 93.2 - (host->EffectiveLatency / 40);
1394 } else {
1395 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1396 }
1397
1398 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1399 // loss of 5% will be entered as 5).
1400 R = R - (pl * 2.5);
1401
1402 if (R < 0) {
1403 R = 0;
1404 }
1405
1406 host->score = R;
1407 host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
1408 } else {
1409 host->jitter = 0;
1410 host->jitter_min = 0;
1411 host->jitter_max = 0;
1412 host->mos = 0;
1413 }
1414
1415 host->pl = pl;
1416 host->rta = rta;
1417
1418 /* if no new mode selected, use old schema */
1419 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1420 rta_mode = true;
1421 pl_mode = true;
1422 }
1423
1424 /* Check which mode is on and do the warn / Crit stuff */
1425 if (rta_mode) {
1426 if (rta >= crit.rta) {
1427 this_status = STATE_CRITICAL;
1428 status = STATE_CRITICAL;
1429 host->rta_status = STATE_CRITICAL;
1430 } else if (status != STATE_CRITICAL && (rta >= warn.rta)) {
1431 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1432 status = STATE_WARNING;
1433 host->rta_status = STATE_WARNING;
1434 }
1435 }
1436
1437 if (pl_mode) {
1438 if (pl >= crit.pl) {
1439 this_status = STATE_CRITICAL;
1440 status = STATE_CRITICAL;
1441 host->pl_status = STATE_CRITICAL;
1442 } else if (status != STATE_CRITICAL && (pl >= warn.pl)) {
1443 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1444 status = STATE_WARNING;
1445 host->pl_status = STATE_WARNING;
1446 }
1447 }
1448
1449 if (jitter_mode) {
1450 if (host->jitter >= crit.jitter) {
1451 this_status = STATE_CRITICAL;
1452 status = STATE_CRITICAL;
1453 host->jitter_status = STATE_CRITICAL;
1454 } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1455 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1456 status = STATE_WARNING;
1457 host->jitter_status = STATE_WARNING;
1458 }
1459 }
1460
1461 if (mos_mode) {
1462 if (host->mos <= crit.mos) {
1463 this_status = STATE_CRITICAL;
1464 status = STATE_CRITICAL;
1465 host->mos_status = STATE_CRITICAL;
1466 } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) {
1467 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1468 status = STATE_WARNING;
1469 host->mos_status = STATE_WARNING;
1470 }
1471 }
1472
1473 if (score_mode) {
1474 if (host->score <= crit.score) {
1475 this_status = STATE_CRITICAL;
1476 status = STATE_CRITICAL;
1477 host->score_status = STATE_CRITICAL;
1478 } else if (status != STATE_CRITICAL && (host->score <= warn.score)) {
1479 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1480 status = STATE_WARNING;
1481 host->score_status = STATE_WARNING;
1482 }
1483 }
1484 1525
1485 if (this_status == STATE_WARNING) { 1526 targets_ok += host_check.targets_ok;
1486 hosts_warn++; 1527 targets_warn += host_check.targets_warn;
1487 } else if (this_status == STATE_OK) {
1488 hosts_ok++;
1489 }
1490 1528
1491 host = host->next; 1529 mp_add_subcheck_to_check(overall, host_check.sc_host);
1492 } 1530 }
1493 1531
1494 /* this is inevitable */
1495 if (!targets_alive) {
1496 status = STATE_CRITICAL;
1497 }
1498 if (min_hosts_alive > -1) { 1532 if (min_hosts_alive > -1) {
1499 if (hosts_ok >= min_hosts_alive) { 1533 mp_subcheck sc_min_targets_alive = mp_subcheck_init();
1500 status = STATE_OK; 1534 sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK);
1501 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { 1535
1502 status = STATE_WARNING; 1536 if (targets_ok >= min_hosts_alive) {
1503 } 1537 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK);
1504 } 1538 xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", targets_ok,
1505 printf("%s - ", status_string[status]); 1539 min_hosts_alive);
1506 1540
1507 host = list; 1541 // Overwrite main state here
1508 while (host) { 1542 overall->evaluation_function = &mp_eval_ok;
1509 if (debug) { 1543 } else if ((targets_ok + targets_warn) >= min_hosts_alive) {
1510 puts(""); 1544 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING);
1511 } 1545 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1512 1546 targets_ok + targets_warn, min_hosts_alive);
1513 if (i) { 1547 overall->evaluation_function = &mp_eval_warning;
1514 if (i < targets) { 1548 } else {
1515 printf(" :: "); 1549 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL);
1516 } else { 1550 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1517 printf("\n"); 1551 targets_ok + targets_warn, min_hosts_alive);
1518 } 1552 overall->evaluation_function = &mp_eval_critical;
1519 }
1520
1521 i++;
1522
1523 if (!host->icmp_recv) {
1524 status = STATE_CRITICAL;
1525 host->rtmin = 0;
1526 host->jitter_min = 0;
1527
1528 if (host->flags & FLAG_LOST_CAUSE) {
1529 char address[INET6_ADDRSTRLEN];
1530 parse_address(&host->error_addr, address, sizeof(address));
1531 printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100);
1532 } else { /* not marked as lost cause, so we have no flags for it */
1533 printf("%s: rta nan, lost 100%%", host->name);
1534 }
1535 } else { /* !icmp_recv */
1536 printf("%s", host->name);
1537 /* rta text output */
1538 if (rta_mode) {
1539 if (status == STATE_OK) {
1540 printf(" rta %0.3fms", host->rta / 1000);
1541 } else if (status == STATE_WARNING && host->rta_status == status) {
1542 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000);
1543 } else if (status == STATE_CRITICAL && host->rta_status == status) {
1544 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000);
1545 }
1546 }
1547
1548 /* pl text output */
1549 if (pl_mode) {
1550 if (status == STATE_OK) {
1551 printf(" lost %u%%", host->pl);
1552 } else if (status == STATE_WARNING && host->pl_status == status) {
1553 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1554 } else if (status == STATE_CRITICAL && host->pl_status == status) {
1555 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1556 }
1557 }
1558
1559 /* jitter text output */
1560 if (jitter_mode) {
1561 if (status == STATE_OK) {
1562 printf(" jitter %0.3fms", (float)host->jitter);
1563 } else if (status == STATE_WARNING && host->jitter_status == status) {
1564 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1565 } else if (status == STATE_CRITICAL && host->jitter_status == status) {
1566 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1567 }
1568 }
1569
1570 /* mos text output */
1571 if (mos_mode) {
1572 if (status == STATE_OK) {
1573 printf(" MOS %0.1f", (float)host->mos);
1574 } else if (status == STATE_WARNING && host->mos_status == status) {
1575 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1576 } else if (status == STATE_CRITICAL && host->mos_status == status) {
1577 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1578 }
1579 }
1580
1581 /* score text output */
1582 if (score_mode) {
1583 if (status == STATE_OK) {
1584 printf(" Score %u", (int)host->score);
1585 } else if (status == STATE_WARNING && host->score_status == status) {
1586 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1587 } else if (status == STATE_CRITICAL && host->score_status == status) {
1588 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1589 }
1590 }
1591
1592 /* order statis text output */
1593 if (order_mode) {
1594 if (status == STATE_OK) {
1595 printf(" Packets in order");
1596 } else if (status == STATE_CRITICAL && host->order_status == status) {
1597 printf(" Packets out of order");
1598 }
1599 }
1600 }
1601 host = host->next;
1602 }
1603
1604 /* iterate once more for pretty perfparse output */
1605 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1606 printf("|");
1607 }
1608 i = 0;
1609 host = list;
1610 while (host) {
1611 if (debug) {
1612 puts("");
1613 }
1614
1615 if (rta_mode) {
1616 if (host->pl < 100) {
1617 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "",
1618 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, (targets > 1) ? host->name : "",
1619 (float)host->rtmax / 1000, (targets > 1) ? host->name : "",
1620 (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1621 } else {
1622 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "",
1623 (targets > 1) ? host->name : "");
1624 }
1625 }
1626
1627 if (pl_mode) {
1628 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1629 }
1630
1631 if (jitter_mode) {
1632 if (host->pl < 100) {
1633 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1634 (targets > 1) ? host->name : "", (float)host->jitter, (float)warn.jitter, (float)crit.jitter,
1635 (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1636 (float)host->jitter_min / 1000);
1637 } else {
1638 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "",
1639 (targets > 1) ? host->name : "", (targets > 1) ? host->name : "");
1640 }
1641 }
1642
1643 if (mos_mode) {
1644 if (host->pl < 100) {
1645 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos);
1646 } else {
1647 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1648 }
1649 }
1650
1651 if (score_mode) {
1652 if (host->pl < 100) {
1653 printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score);
1654 } else {
1655 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1656 }
1657 } 1553 }
1658 1554
1659 host = host->next; 1555 mp_add_subcheck_to_check(overall, sc_min_targets_alive);
1660 }
1661
1662 if (min_hosts_alive > -1) {
1663 if (hosts_ok >= min_hosts_alive) {
1664 status = STATE_OK;
1665 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) {
1666 status = STATE_WARNING;
1667 }
1668 } 1556 }
1669 1557
1670 /* finish with an empty line */ 1558 /* finish with an empty line */
1671 puts("");
1672 if (debug) { 1559 if (debug) {
1673 printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok, 1560 printf(
1674 hosts_warn, min_hosts_alive); 1561 "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1562 number_of_targets, targets_alive(number_of_targets, program_state->targets_down),
1563 targets_ok, targets_warn, min_hosts_alive);
1675 } 1564 }
1676
1677 exit(status);
1678} 1565}
1679 1566
1680static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { 1567static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) {
1681 u_int ret;
1682 struct timeval now;
1683
1684 if (!later) {
1685 gettimeofday(&now, &tz);
1686 later = &now;
1687 }
1688 if (!early) {
1689 early = &prog_start;
1690 }
1691
1692 /* if early > later we return 0 so as to indicate a timeout */ 1568 /* if early > later we return 0 so as to indicate a timeout */
1693 if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { 1569 if (earlier.tv_sec > later.tv_sec ||
1570 (earlier.tv_sec == later.tv_sec && earlier.tv_usec > later.tv_usec)) {
1694 return 0; 1571 return 0;
1695 } 1572 }
1696 ret = (later->tv_sec - early->tv_sec) * 1000000; 1573
1697 ret += later->tv_usec - early->tv_usec; 1574 time_t ret = (later.tv_sec - earlier.tv_sec) * 1000000;
1575 ret += later.tv_usec - earlier.tv_usec;
1698 1576
1699 return ret; 1577 return ret;
1700} 1578}
1701 1579
1702static int add_target_ip(char *arg, struct sockaddr_storage *in) { 1580static time_t get_timevaldiff_to_now(struct timeval earlier) {
1703 struct rta_host *host; 1581 struct timeval now;
1704 struct sockaddr_in *sin, *host_sin; 1582 gettimeofday(&now, NULL);
1705 struct sockaddr_in6 *sin6, *host_sin6; 1583
1584 return get_timevaldiff(earlier, now);
1585}
1586
1587static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) {
1588 assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6));
1706 1589
1707 if (address_family == AF_INET) { 1590 if (debug) {
1708 sin = (struct sockaddr_in *)in; 1591 char straddr[INET6_ADDRSTRLEN];
1592 parse_address((&address), straddr, sizeof(straddr));
1593 printf("add_target_ip called with: %s\n", straddr);
1594 }
1595 struct sockaddr_in *sin;
1596 struct sockaddr_in6 *sin6;
1597 if (address.ss_family == AF_INET) {
1598 sin = (struct sockaddr_in *)&address;
1599 } else if (address.ss_family == AF_INET6) {
1600 sin6 = (struct sockaddr_in6 *)&address;
1709 } else { 1601 } else {
1710 sin6 = (struct sockaddr_in6 *)in; 1602 assert(false);
1711 } 1603 }
1712 1604
1605 add_target_ip_wrapper result = {
1606 .error_code = OK,
1607 .target = NULL,
1608 };
1609
1713 /* disregard obviously stupid addresses 1610 /* disregard obviously stupid addresses
1714 * (I didn't find an ipv6 equivalent to INADDR_NONE) */ 1611 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1715 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || 1612 if (((address.ss_family == AF_INET &&
1716 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { 1613 (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1717 return -1; 1614 (address.ss_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1615 result.error_code = ERROR;
1616 return result;
1718 } 1617 }
1719 1618
1720 /* no point in adding two identical IP's, so don't. ;) */ 1619 // get string representation of address
1721 host = list; 1620 char straddr[INET6_ADDRSTRLEN];
1722 while (host) { 1621 parse_address((&address), straddr, sizeof(straddr));
1723 host_sin = (struct sockaddr_in *)&host->saddr_in;
1724 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1725
1726 if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) ||
1727 (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1728 if (debug) {
1729 printf("Identical IP already exists. Not adding %s\n", arg);
1730 }
1731 return -1;
1732 }
1733 host = host->next;
1734 }
1735 1622
1736 /* add the fresh ip */ 1623 /* add the fresh ip */
1737 host = (struct rta_host *)malloc(sizeof(struct rta_host)); 1624 ping_target *target = (ping_target *)calloc(1, sizeof(ping_target));
1738 if (!host) { 1625 if (!target) {
1739 char straddr[INET6_ADDRSTRLEN]; 1626 crash("add_target_ip(%s): malloc(%lu) failed", straddr, sizeof(ping_target));
1740 parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr));
1741 crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host));
1742 } 1627 }
1743 memset(host, 0, sizeof(struct rta_host));
1744 1628
1745 /* set the values. use calling name for output */ 1629 ping_target_create_wrapper target_wrapper = ping_target_create(address);
1746 host->name = strdup(arg);
1747 1630
1748 /* fill out the sockaddr_storage struct */ 1631 if (target_wrapper.errorcode == OK) {
1749 if (address_family == AF_INET) { 1632 *target = target_wrapper.host;
1750 host_sin = (struct sockaddr_in *)&host->saddr_in; 1633 result.target = target;
1751 host_sin->sin_family = AF_INET;
1752 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1753 } else { 1634 } else {
1754 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; 1635 result.error_code = target_wrapper.errorcode;
1755 host_sin6->sin6_family = AF_INET6;
1756 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1757 }
1758
1759 /* fill out the sockaddr_in struct */
1760 host->rtmin = INFINITY;
1761 host->rtmax = 0;
1762 host->jitter = 0;
1763 host->jitter_max = 0;
1764 host->jitter_min = INFINITY;
1765 host->last_tdiff = 0;
1766 host->order_status = STATE_OK;
1767 host->last_icmp_seq = 0;
1768 host->rta_status = 0;
1769 host->pl_status = 0;
1770 host->jitter_status = 0;
1771 host->mos_status = 0;
1772 host->score_status = 0;
1773 host->pl_status = 0;
1774
1775 if (!list) {
1776 list = cursor = host;
1777 } else {
1778 cursor->next = host;
1779 } 1636 }
1780 1637
1781 cursor = host; 1638 return result;
1782 targets++;
1783
1784 return 0;
1785} 1639}
1786 1640
1787/* wrapper for add_target_ip */ 1641/* wrapper for add_target_ip */
1788static int add_target(char *arg) { 1642static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode,
1789 int error, result = -1; 1643 sa_family_t enforced_proto) {
1790 struct sockaddr_storage ip; 1644 if (debug > 0) {
1791 struct addrinfo hints, *res, *p; 1645 printf("add_target called with argument %s\n", arg);
1792 struct sockaddr_in *sin; 1646 }
1793 struct sockaddr_in6 *sin6; 1647
1794 1648 struct sockaddr_storage address_storage = {};
1795 switch (address_family) { 1649 struct sockaddr_in *sin = NULL;
1796 case -1: 1650 struct sockaddr_in6 *sin6 = NULL;
1797 /* -4 and -6 are not specified on cmdline */ 1651 int error_code = -1;
1798 address_family = AF_INET; 1652
1799 sin = (struct sockaddr_in *)&ip; 1653 switch (enforced_proto) {
1800 result = inet_pton(address_family, arg, &sin->sin_addr); 1654 case AF_UNSPEC:
1801#ifdef USE_IPV6 1655 /*
1802 if (result != 1) { 1656 * no enforced protocol family
1803 address_family = AF_INET6; 1657 * try to parse the address with each one
1804 sin6 = (struct sockaddr_in6 *)&ip; 1658 */
1805 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1659 sin = (struct sockaddr_in *)&address_storage;
1806 } 1660 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1807#endif 1661 address_storage.ss_family = AF_INET;
1808 /* If we don't find any valid addresses, we still don't know the address_family */ 1662
1809 if (result != 1) { 1663 if (error_code != 1) {
1810 address_family = -1; 1664 sin6 = (struct sockaddr_in6 *)&address_storage;
1665 error_code = inet_pton(AF_INET6, arg, &sin6->sin6_addr);
1666 address_storage.ss_family = AF_INET6;
1811 } 1667 }
1812 break; 1668 break;
1813 case AF_INET: 1669 case AF_INET:
1814 sin = (struct sockaddr_in *)&ip; 1670 sin = (struct sockaddr_in *)&address_storage;
1815 result = inet_pton(address_family, arg, &sin->sin_addr); 1671 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1672 address_storage.ss_family = AF_INET;
1816 break; 1673 break;
1817 case AF_INET6: 1674 case AF_INET6:
1818 sin6 = (struct sockaddr_in6 *)&ip; 1675 sin6 = (struct sockaddr_in6 *)&address_storage;
1819 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1676 error_code = inet_pton(AF_INET, arg, &sin6->sin6_addr);
1677 address_storage.ss_family = AF_INET6;
1820 break; 1678 break;
1821 default: 1679 default:
1822 crash("Address family not supported"); 1680 crash("Address family not supported");
1823 } 1681 }
1824 1682
1825 /* don't resolve if we don't have to */ 1683 add_target_wrapper result = {
1826 if (result == 1) { 1684 .error_code = OK,
1685 .targets = NULL,
1686 .has_v4 = false,
1687 .has_v6 = false,
1688 };
1689
1690 // if error_code == 1 the address was a valid address parsed above
1691 if (error_code == 1) {
1827 /* don't add all ip's if we were given a specific one */ 1692 /* don't add all ip's if we were given a specific one */
1828 return add_target_ip(arg, &ip); 1693 add_target_ip_wrapper targeted = add_target_ip(address_storage);
1829 } else { 1694
1830 errno = 0; 1695 if (targeted.error_code != OK) {
1831 memset(&hints, 0, sizeof(hints)); 1696 result.error_code = ERROR;
1832 if (address_family == -1) { 1697 return result;
1833 hints.ai_family = AF_UNSPEC;
1834 } else {
1835 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1836 } 1698 }
1837 hints.ai_socktype = SOCK_RAW; 1699
1838 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { 1700 if (targeted.target->address.ss_family == AF_INET) {
1839 errno = 0; 1701 result.has_v4 = true;
1840 crash("Failed to resolve %s: %s", arg, gai_strerror(error)); 1702 } else if (targeted.target->address.ss_family == AF_INET6) {
1841 return -1; 1703 result.has_v6 = true;
1704 } else {
1705 assert(false);
1842 } 1706 }
1843 address_family = res->ai_family; 1707 result.targets = targeted.target;
1708 result.number_of_targets = 1;
1709 return result;
1710 }
1711
1712 struct addrinfo hints = {};
1713 errno = 0;
1714 hints.ai_family = enforced_proto;
1715 hints.ai_socktype = SOCK_RAW;
1716
1717 int error;
1718 struct addrinfo *res;
1719 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1720 errno = 0;
1721 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1722 result.error_code = ERROR;
1723 return result;
1844 } 1724 }
1845 1725
1846 /* possibly add all the IP's as targets */ 1726 /* possibly add all the IP's as targets */
1847 for (p = res; p != NULL; p = p->ai_next) { 1727 for (struct addrinfo *address = res; address != NULL; address = address->ai_next) {
1848 memcpy(&ip, p->ai_addr, p->ai_addrlen); 1728 struct sockaddr_storage temporary_ip_address;
1849 add_target_ip(arg, &ip); 1729 memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen);
1730
1731 add_target_ip_wrapper tmp = add_target_ip(temporary_ip_address);
1732
1733 if (tmp.error_code != OK) {
1734 // No proper error handling
1735 // What to do?
1736 } else {
1737 if (result.targets == NULL) {
1738 result.targets = tmp.target;
1739 result.number_of_targets = 1;
1740 } else {
1741 result.number_of_targets += ping_target_list_append(result.targets, tmp.target);
1742 }
1743 if (address->ai_family == AF_INET) {
1744 result.has_v4 = true;
1745 } else if (address->ai_family == AF_INET6) {
1746 result.has_v6 = true;
1747 }
1748 }
1850 1749
1851 /* this is silly, but it works */ 1750 /* this is silly, but it works */
1852 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1751 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1855,18 +1754,20 @@ static int add_target(char *arg) {
1855 } 1754 }
1856 continue; 1755 continue;
1857 } 1756 }
1757
1758 // Abort after first hit if not in of the modes above
1858 break; 1759 break;
1859 } 1760 }
1860 freeaddrinfo(res); 1761 freeaddrinfo(res);
1861 1762
1862 return 0; 1763 return result;
1863} 1764}
1864 1765
1865static void set_source_ip(char *arg) { 1766static void set_source_ip(char *arg, const int icmp_sock, sa_family_t addr_family) {
1866 struct sockaddr_in src; 1767 struct sockaddr_in src;
1867 1768
1868 memset(&src, 0, sizeof(src)); 1769 memset(&src, 0, sizeof(src));
1869 src.sin_family = address_family; 1770 src.sin_family = addr_family;
1870 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) { 1771 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) {
1871 src.sin_addr.s_addr = get_ip_address(arg); 1772 src.sin_addr.s_addr = get_ip_address(arg);
1872 } 1773 }
@@ -1878,8 +1779,8 @@ static void set_source_ip(char *arg) {
1878/* TODO: Move this to netutils.c and also change check_dhcp to use that. */ 1779/* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1879static in_addr_t get_ip_address(const char *ifname) { 1780static in_addr_t get_ip_address(const char *ifname) {
1880 // TODO: Rewrite this so the function return an error and we exit somewhere else 1781 // TODO: Rewrite this so the function return an error and we exit somewhere else
1881 struct sockaddr_in ip; 1782 struct sockaddr_in ip_address;
1882 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy 1783 ip_address.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1883#if defined(SIOCGIFADDR) 1784#if defined(SIOCGIFADDR)
1884 struct ifreq ifr; 1785 struct ifreq ifr;
1885 1786
@@ -1897,7 +1798,7 @@ static in_addr_t get_ip_address(const char *ifname) {
1897 errno = 0; 1798 errno = 0;
1898 crash("Cannot get interface IP address on this platform."); 1799 crash("Cannot get interface IP address on this platform.");
1899#endif 1800#endif
1900 return ip.sin_addr.s_addr; 1801 return ip_address.sin_addr.s_addr;
1901} 1802}
1902 1803
1903/* 1804/*
@@ -1906,103 +1807,127 @@ static in_addr_t get_ip_address(const char *ifname) {
1906 * s = seconds 1807 * s = seconds
1907 * return value is in microseconds 1808 * return value is in microseconds
1908 */ 1809 */
1909static u_int get_timevar(const char *str) { 1810static get_timevar_wrapper get_timevar(const char *str) {
1910 char p, u, *ptr; 1811 get_timevar_wrapper result = {
1911 size_t len; 1812 .error_code = OK,
1912 u_int i, d; /* integer and decimal, respectively */ 1813 .time_range = 0,
1913 u_int factor = 1000; /* default to milliseconds */ 1814 };
1914 1815
1915 if (!str) { 1816 if (!str) {
1916 return 0; 1817 result.error_code = ERROR;
1818 return result;
1917 } 1819 }
1918 len = strlen(str); 1820
1821 size_t len = strlen(str);
1919 if (!len) { 1822 if (!len) {
1920 return 0; 1823 result.error_code = ERROR;
1824 return result;
1921 } 1825 }
1922 1826
1923 /* unit might be given as ms|m (millisec), 1827 /* unit might be given as ms|m (millisec),
1924 * us|u (microsec) or just plain s, for seconds */ 1828 * us|u (microsec) or just plain s, for seconds */
1925 p = '\0'; 1829 char tmp = '\0';
1926 u = str[len - 1]; 1830 char unit = str[len - 1];
1927 if (len >= 2 && !isdigit((int)str[len - 2])) { 1831 if (len >= 2 && !isdigit((int)str[len - 2])) {
1928 p = str[len - 2]; 1832 tmp = str[len - 2];
1929 } 1833 }
1930 if (p && u == 's') { 1834
1931 u = p; 1835 if (tmp && unit == 's') {
1932 } else if (!p) { 1836 unit = tmp;
1933 p = u; 1837 } else if (!tmp) {
1838 tmp = unit;
1934 } 1839 }
1840
1935 if (debug > 2) { 1841 if (debug > 2) {
1936 printf("evaluating %s, u: %c, p: %c\n", str, u, p); 1842 printf("evaluating %s, u: %c, p: %c\n", str, unit, tmp);
1937 } 1843 }
1938 1844
1939 if (u == 'u') { 1845 unsigned int factor = 1000; /* default to milliseconds */
1846 if (unit == 'u') {
1940 factor = 1; /* microseconds */ 1847 factor = 1; /* microseconds */
1941 } else if (u == 'm') { 1848 } else if (unit == 'm') {
1942 factor = 1000; /* milliseconds */ 1849 factor = 1000; /* milliseconds */
1943 } else if (u == 's') { 1850 } else if (unit == 's') {
1944 factor = 1000000; /* seconds */ 1851 factor = 1000000; /* seconds */
1945 } 1852 }
1853
1946 if (debug > 2) { 1854 if (debug > 2) {
1947 printf("factor is %u\n", factor); 1855 printf("factor is %u\n", factor);
1948 } 1856 }
1949 1857
1950 i = strtoul(str, &ptr, 0); 1858 char *ptr;
1859 unsigned long pre_radix;
1860 pre_radix = strtoul(str, &ptr, 0);
1951 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { 1861 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) {
1952 return i * factor; 1862 result.time_range = (unsigned int)(pre_radix * factor);
1863 return result;
1953 } 1864 }
1954 1865
1955 /* time specified in usecs can't have decimal points, so ignore them */ 1866 /* time specified in usecs can't have decimal points, so ignore them */
1956 if (factor == 1) { 1867 if (factor == 1) {
1957 return i; 1868 result.time_range = (unsigned int)pre_radix;
1869 return result;
1958 } 1870 }
1959 1871
1960 d = strtoul(ptr + 1, NULL, 0); 1872 /* integer and decimal, respectively */
1873 unsigned int post_radix = (unsigned int)strtoul(ptr + 1, NULL, 0);
1961 1874
1962 /* d is decimal, so get rid of excess digits */ 1875 /* d is decimal, so get rid of excess digits */
1963 while (d >= factor) { 1876 while (post_radix >= factor) {
1964 d /= 10; 1877 post_radix /= 10;
1965 } 1878 }
1966 1879
1967 /* the last parenthesis avoids floating point exceptions. */ 1880 /* the last parenthesis avoids floating point exceptions. */
1968 return ((i * factor) + (d * (factor / 10))); 1881 result.time_range = (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10)));
1882 return result;
1969} 1883}
1970 1884
1971/* not too good at checking errors, but it'll do (main() should barfe on -1) */ 1885static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) {
1972static int get_threshold(char *str, threshold *th) { 1886 get_threshold_wrapper result = {
1973 char *p = NULL, i = 0; 1887 .errorcode = OK,
1888 .threshold = threshold,
1889 };
1974 1890
1975 if (!str || !strlen(str) || !th) { 1891 if (!str || !strlen(str)) {
1976 return -1; 1892 result.errorcode = ERROR;
1893 return result;
1977 } 1894 }
1978 1895
1979 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ 1896 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1980 p = &str[strlen(str) - 1]; 1897 bool is_at_last_char = false;
1981 while (p != &str[1]) { 1898 char *tmp = &str[strlen(str) - 1];
1982 if (*p == '%') { 1899 while (tmp != &str[1]) {
1983 *p = '\0'; 1900 if (*tmp == '%') {
1984 } else if (*p == ',' && i) { 1901 *tmp = '\0';
1985 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1902 } else if (*tmp == ',' && is_at_last_char) {
1986 th->pl = (unsigned char)strtoul(p + 1, NULL, 0); 1903 *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */
1904 result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0);
1987 break; 1905 break;
1988 } 1906 }
1989 i = 1; 1907 is_at_last_char = true;
1990 p--; 1908 tmp--;
1991 } 1909 }
1992 th->rta = get_timevar(str);
1993 1910
1994 if (!th->rta) { 1911 get_timevar_wrapper parsed_time = get_timevar(str);
1995 return -1; 1912
1913 if (parsed_time.error_code == OK) {
1914 result.threshold.rta = parsed_time.time_range;
1915 } else {
1916 if (debug > 1) {
1917 printf("%s: failed to parse rta threshold\n", __FUNCTION__);
1918 }
1919 result.errorcode = ERROR;
1920 return result;
1996 } 1921 }
1997 1922
1998 if (th->rta > MAXTTL * 1000000) { 1923 if (result.threshold.rta > MAXTTL * 1000000) {
1999 th->rta = MAXTTL * 1000000; 1924 result.threshold.rta = MAXTTL * 1000000;
2000 } 1925 }
2001 if (th->pl > 100) { 1926 if (result.threshold.pl > 100) {
2002 th->pl = 100; 1927 result.threshold.pl = 100;
2003 } 1928 }
2004 1929
2005 return 0; 1930 return result;
2006} 1931}
2007 1932
2008/* 1933/*
@@ -2013,184 +1938,537 @@ static int get_threshold(char *str, threshold *th) {
2013 * @param[in] length strlen(str) 1938 * @param[in] length strlen(str)
2014 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned 1939 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
2015 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned 1940 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
2016 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively) 1941 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score
1942 * (exclusively)
2017 */ 1943 */
2018static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { 1944static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
2019 if (!str || !length || !warn || !crit) { 1945 check_icmp_threshold crit, threshold_mode mode) {
2020 return false; 1946 get_threshold2_wrapper result = {
1947 .errorcode = OK,
1948 .warn = warn,
1949 .crit = crit,
1950 };
1951
1952 if (!str || !length) {
1953 result.errorcode = ERROR;
1954 return result;
2021 } 1955 }
2022 1956
2023 // p points to the last char in str 1957 // p points to the last char in str
2024 char *p = &str[length - 1]; 1958 char *work_pointer = &str[length - 1];
2025 1959
2026 // first_iteration is bof-stop on stupid libc's 1960 // first_iteration is bof-stop on stupid libc's
2027 bool first_iteration = true; 1961 bool first_iteration = true;
2028 1962
2029 while (p != &str[0]) { 1963 while (work_pointer != &str[0]) {
2030 if ((*p == 'm') || (*p == '%')) { 1964 if ((*work_pointer == 'm') || (*work_pointer == '%')) {
2031 *p = '\0'; 1965 *work_pointer = '\0';
2032 } else if (*p == ',' && !first_iteration) { 1966 } else if (*work_pointer == ',' && !first_iteration) {
2033 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1967 *work_pointer = '\0'; /* reset it so get_timevar(str) works nicely later */
2034 1968
2035 char *start_of_value = p + 1; 1969 char *start_of_value = work_pointer + 1;
2036 1970
2037 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { 1971 parse_threshold2_helper_wrapper tmp =
2038 return false; 1972 parse_threshold2_helper(start_of_value, strlen(start_of_value), result.crit, mode);
1973 if (tmp.errorcode != OK) {
1974 result.errorcode = ERROR;
1975 return result;
2039 } 1976 }
1977 result.crit = tmp.result;
2040 } 1978 }
2041 first_iteration = false; 1979 first_iteration = false;
2042 p--; 1980 work_pointer--;
2043 } 1981 }
2044 1982
2045 return parse_threshold2_helper(p, strlen(p), warn, mode); 1983 parse_threshold2_helper_wrapper tmp =
1984 parse_threshold2_helper(work_pointer, strlen(work_pointer), result.warn, mode);
1985 if (tmp.errorcode != OK) {
1986 result.errorcode = ERROR;
1987 } else {
1988 result.warn = tmp.result;
1989 }
1990 return result;
2046} 1991}
2047 1992
2048static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { 1993static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
1994 size_t length,
1995 check_icmp_threshold thr,
1996 threshold_mode mode) {
2049 char *resultChecker = {0}; 1997 char *resultChecker = {0};
1998 parse_threshold2_helper_wrapper result = {
1999 .result = thr,
2000 .errorcode = OK,
2001 };
2050 2002
2051 switch (mode) { 2003 switch (mode) {
2052 case const_rta_mode: 2004 case const_rta_mode:
2053 thr->rta = strtod(s, &resultChecker) * 1000; 2005 result.result.rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000);
2054 break; 2006 break;
2055 case const_packet_loss_mode: 2007 case const_packet_loss_mode:
2056 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0); 2008 result.result.pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0);
2057 break; 2009 break;
2058 case const_jitter_mode: 2010 case const_jitter_mode:
2059 thr->jitter = strtod(s, &resultChecker); 2011 result.result.jitter = strtod(threshold_string, &resultChecker);
2060
2061 break; 2012 break;
2062 case const_mos_mode: 2013 case const_mos_mode:
2063 thr->mos = strtod(s, &resultChecker); 2014 result.result.mos = strtod(threshold_string, &resultChecker);
2064 break; 2015 break;
2065 case const_score_mode: 2016 case const_score_mode:
2066 thr->score = strtod(s, &resultChecker); 2017 result.result.score = strtod(threshold_string, &resultChecker);
2067 break; 2018 break;
2068 } 2019 }
2069 2020
2070 if (resultChecker == s) { 2021 if (resultChecker == threshold_string) {
2071 // Failed to parse 2022 // Failed to parse
2072 return false; 2023 result.errorcode = ERROR;
2024 return result;
2073 } 2025 }
2074 2026
2075 if (resultChecker != (s + length)) { 2027 if (resultChecker != (threshold_string + length)) {
2076 // Trailing symbols 2028 // Trailing symbols
2077 return false; 2029 result.errorcode = ERROR;
2078 } 2030 }
2079 2031
2080 return true; 2032 return result;
2081} 2033}
2082 2034
2083unsigned short icmp_checksum(uint16_t *p, size_t n) { 2035unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) {
2084 unsigned short cksum;
2085 long sum = 0; 2036 long sum = 0;
2086 2037
2087 /* sizeof(uint16_t) == 2 */ 2038 /* sizeof(uint16_t) == 2 */
2088 while (n >= 2) { 2039 while (packet_size >= 2) {
2089 sum += *(p++); 2040 sum += *(packet++);
2090 n -= 2; 2041 packet_size -= 2;
2091 } 2042 }
2092 2043
2093 /* mop up the occasional odd byte */ 2044 /* mop up the occasional odd byte */
2094 if (n == 1) { 2045 if (packet_size == 1) {
2095 sum += *((uint8_t *)p - 1); 2046 sum += *((uint8_t *)packet - 1);
2096 } 2047 }
2097 2048
2098 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 2049 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2099 sum += (sum >> 16); /* add carry */ 2050 sum += (sum >> 16); /* add carry */
2100 cksum = ~sum; /* ones-complement, trunc to 16 bits */ 2051 unsigned short cksum;
2052 cksum = (unsigned short)~sum; /* ones-complement, trunc to 16 bits */
2101 2053
2102 return cksum; 2054 return cksum;
2103} 2055}
2104 2056
2105void print_help(void) { 2057void print_help(void) {
2106 /*print_revision (progname);*/ /* FIXME: Why? */ 2058 // print_revision (progname); /* FIXME: Why? */
2107 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n"); 2059 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2108 2060
2109 printf(COPYRIGHT, copyright, email); 2061 printf(COPYRIGHT, copyright, email);
2110 2062
2111 printf("\n\n");
2112
2113 print_usage(); 2063 print_usage();
2114 2064
2115 printf(UT_HELP_VRSN); 2065 printf(UT_HELP_VRSN);
2116 printf(UT_EXTRA_OPTS); 2066 printf(UT_EXTRA_OPTS);
2117 2067
2118 printf(" %s\n", "-H"); 2068 printf(" -H, --Host=HOST\n");
2119 printf(" %s\n", _("specify a target")); 2069 printf(" %s\n",
2120 printf(" %s\n", "[-4|-6]"); 2070 _("specify a target, might be one of: resolveable name | IPv6 address | IPv4 address\n"
2121 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); 2071 " (required, can be given multiple times)"));
2122 printf(" %s\n", "-w"); 2072 printf(" %s\n", "[-4|-6], [--ipv4-only|--ipv6-only]");
2123 printf(" %s", _("warning threshold (currently ")); 2073 printf(" %s\n", _("Use IPv4 or IPv6 only to communicate with the targets"));
2124 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 2074 printf(" %s\n", "-w, --warning=WARN_VALUE");
2125 printf(" %s\n", "-c"); 2075 printf(" %s", _("warning threshold (default "));
2126 printf(" %s", _("critical threshold (currently ")); 2076 printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL);
2127 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); 2077 printf(" %s\n", "-c, --critical=CRIT_VALUE");
2128 2078 printf(" %s", _("critical threshold (default "));
2129 printf(" %s\n", "-R"); 2079 printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL);
2130 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); 2080
2131 printf(" %s\n", "-P"); 2081 printf(" %s\n", "-R, --rta-mode-thresholds=RTA_THRESHOLDS");
2082 printf(" %s\n",
2083 _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms"));
2084 printf(" %s\n", "-P, --packet-loss-mode-thresholds=PACKET_LOSS_THRESHOLD");
2132 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); 2085 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2133 printf(" %s\n", "-J"); 2086 printf(" %s\n", "-J, --jitter-mode-thresholds=JITTER_MODE_THRESHOLD");
2134 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms ")); 2087 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2135 printf(" %s\n", "-M"); 2088 printf(" %s\n", "-M, --mos-mode-thresholds=MOS_MODE_THRESHOLD");
2136 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0")); 2089 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2137 printf(" %s\n", "-S"); 2090 printf(" %s\n", "-S, --score-mode-thresholds=SCORE_MODE_THRESHOLD");
2138 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 ")); 2091 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2139 printf(" %s\n", "-O"); 2092 printf(" %s\n", "-O, --out-of-order-packets");
2140 printf(" %s\n", _("detect out of order ICMP packts ")); 2093 printf(
2141 printf(" %s\n", "-H"); 2094 " %s\n",
2142 printf(" %s\n", _("specify a target")); 2095 _("detect out of order ICMP packets, if such packets are found, the result is CRITICAL"));
2143 printf(" %s\n", "-s"); 2096 printf(" %s\n", "[-n|-p], --number-of-packets=NUMBER_OF_PACKETS");
2144 printf(" %s\n", _("specify a source IP address or device name")); 2097 printf(" %s", _("number of packets to send (default "));
2145 printf(" %s\n", "-n"); 2098 printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS);
2146 printf(" %s", _("number of packets to send (currently ")); 2099
2147 printf("%u)\n", packets);
2148 printf(" %s\n", "-p");
2149 printf(" %s", _("number of packets to send (currently "));
2150 printf("%u)\n", packets);
2151 printf(" %s\n", "-i"); 2100 printf(" %s\n", "-i");
2152 printf(" %s", _("max packet interval (currently ")); 2101 printf(" %s", _("[DEPRECATED] packet interval (default "));
2153 printf("%0.3fms)\n", (float)pkt_interval / 1000); 2102 printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000);
2154 printf(" %s\n", "-I"); 2103 printf(" %s", _("This option was never actually used and is just mentioned here for "
2155 printf(" %s", _("max target interval (currently ")); 2104 "historical purposes\n"));
2156 printf("%0.3fms)\n", (float)target_interval / 1000); 2105
2157 printf(" %s\n", "-m"); 2106 printf(" %s\n", "-I, --target-interval=TARGET_INTERVAL");
2158 printf(" %s", _("number of alive hosts required for success")); 2107 printf(" %s%0.3fms)\n The time interval to wait in between one target and the next\n",
2108 _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000);
2109 printf(" %s\n", "-m, --minimal-host-alive=MIN_ALIVE");
2110 printf(" %s", _("number of alive hosts required for success. If less than MIN_ALIVE hosts "
2111 "are OK, but MIN_ALIVE hosts are WARNING or OK, WARNING, else CRITICAL"));
2159 printf("\n"); 2112 printf("\n");
2160 printf(" %s\n", "-l"); 2113 printf(" %s\n", "-l, --outgoing-ttl=OUTGOING_TTL");
2161 printf(" %s", _("TTL on outgoing packets (currently ")); 2114 printf(" %s", _("TTL on outgoing packets (default "));
2162 printf("%u)\n", ttl); 2115 printf("%u)\n", DEFAULT_TTL);
2163 printf(" %s\n", "-t"); 2116 printf(" %s\n", "-b, --size=SIZE");
2164 printf(" %s", _("timeout value (seconds, currently ")); 2117 printf(" %s\n", _("Number of icmp ping data bytes to send"));
2165 printf("%u)\n", timeout); 2118 printf(" %s %lu + %d)\n", _("Packet size will be SIZE + icmp header (default"),
2166 printf(" %s\n", "-b"); 2119 DEFAULT_PING_DATA_SIZE, ICMP_MINLEN);
2167 printf(" %s\n", _("Number of icmp data bytes to send")); 2120 printf(" %s\n", "-v, --verbose");
2168 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN); 2121 printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)"));
2169 printf(" %s\n", "-v"); 2122
2170 printf(" %s\n", _("verbose")); 2123 printf(UT_OUTPUT_FORMAT);
2124
2171 printf("\n"); 2125 printf("\n");
2172 printf("%s\n", _("Notes:")); 2126 printf("%s\n", _("Notes:"));
2173 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P")); 2127 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2174 printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); 2128 printf(" %s\n", _("Naming a host (or several) to check is not."));
2175 printf("\n"); 2129 printf("\n");
2176 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); 2130 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2177 printf(" %s\n", _("packet loss. The default values should work well for most users.")); 2131 printf(" %s\n", _("packet loss. The default values should work well for most users."));
2178 printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations")); 2132 printf(" %s\n",
2179 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); 2133 _("You can specify different RTA factors using the standardized abbreviations"));
2180 /* -d not yet implemented */ 2134 printf(" %s\n",
2181 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); 2135 _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2182 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2183 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2184 printf("\n");
2185 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2186 /* printf ("%s\n", _("Long options are currently unsupported."));
2187 printf ("%s\n", _("Options marked with * require an argument"));
2188 */
2189 2136
2190 printf(UT_SUPPORT); 2137 printf(UT_SUPPORT);
2191} 2138}
2192 2139
2193void print_usage(void) { 2140void print_usage(void) {
2194 printf("%s\n", _("Usage:")); 2141 printf("%s\n", _("Usage:"));
2195 printf(" %s [options] [-H] host1 host2 hostN\n", progname); 2142 printf(" %s [options] [-H host1 [-H host2 [-H hostN]]]\n", progname);
2143}
2144
2145static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
2146 sa_family_t enforced_proto) {
2147 if (debug) {
2148 printf("add_host called with argument %s\n", arg);
2149 }
2150
2151 add_host_wrapper result = {
2152 .error_code = OK,
2153 .host = check_icmp_target_container_init(),
2154 .has_v4 = false,
2155 .has_v6 = false,
2156 };
2157
2158 add_target_wrapper targets = add_target(arg, mode, enforced_proto);
2159
2160 if (targets.error_code != OK) {
2161 result.error_code = targets.error_code;
2162 return result;
2163 }
2164
2165 result.has_v4 = targets.has_v4;
2166 result.has_v6 = targets.has_v6;
2167
2168 result.host = check_icmp_target_container_init();
2169
2170 result.host.name = strdup(arg);
2171 result.host.target_list = targets.targets;
2172 result.host.number_of_targets = targets.number_of_targets;
2173
2174 return result;
2175}
2176
2177mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
2178 check_icmp_threshold warn, check_icmp_threshold crit) {
2179 /* if no new mode selected, use old schema */
2180 if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode &&
2181 !modes.mos_mode && !modes.order_mode) {
2182 modes.rta_mode = true;
2183 modes.pl_mode = true;
2184 }
2185
2186 mp_subcheck result = mp_subcheck_init();
2187 result = mp_set_subcheck_default_state(result, STATE_OK);
2188
2189 char address[INET6_ADDRSTRLEN];
2190 memset(address, 0, INET6_ADDRSTRLEN);
2191 parse_address(&target.address, address, sizeof(address));
2192
2193 xasprintf(&result.output, "%s", address);
2194
2195 double packet_loss;
2196 time_t rta;
2197 if (!target.icmp_recv) {
2198 /* rta 0 is of course not entirely correct, but will still show up
2199 * conspicuously as missing entries in perfparse and cacti */
2200 packet_loss = 100;
2201 rta = 0;
2202 result = mp_set_subcheck_state(result, STATE_CRITICAL);
2203 /* up the down counter if not already counted */
2204
2205 if (target.flags & FLAG_LOST_CAUSE) {
2206 xasprintf(&result.output, "%s: %s @ %s", result.output,
2207 get_icmp_error_msg(target.icmp_type, target.icmp_code), address);
2208 } else { /* not marked as lost cause, so we have no flags for it */
2209 xasprintf(&result.output, "%s", result.output);
2210 }
2211 } else {
2212 packet_loss =
2213 (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent;
2214 rta = target.time_waited / target.icmp_recv;
2215 }
2216
2217 double EffectiveLatency;
2218 double mos; /* Mean opinion score */
2219 double score; /* score */
2220
2221 if (target.icmp_recv > 1) {
2222 /*
2223 * This algorithm is probably pretty much blindly copied from
2224 * locations like this one:
2225 * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS
2226 * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick
2227 * research MOS originates from the Audio/Video transport network area. Whether it can
2228 * and should be computed from ICMP data, I can not say.
2229 *
2230 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
2231 *
2232 * MOS stands likely for Mean Opinion Score (
2233 * https://en.wikipedia.org/wiki/Mean_Opinion_Score )
2234 *
2235 * More links:
2236 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
2237 */
2238 target.jitter = (target.jitter / (target.icmp_recv - 1) / 1000);
2239
2240 /*
2241 * Take the average round trip latency (in milliseconds), add
2242 * round trip jitter, but double the impact to latency
2243 * then add 10 for protocol latencies (in milliseconds).
2244 */
2245 EffectiveLatency = ((double)rta / 1000) + target.jitter * 2 + 10;
2246
2247 double R;
2248 if (EffectiveLatency < 160) {
2249 R = 93.2 - (EffectiveLatency / 40);
2250 } else {
2251 R = 93.2 - ((EffectiveLatency - 120) / 10);
2252 }
2253
2254 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
2255 // loss of 5% will be entered as 5).
2256 R = R - (packet_loss * 2.5);
2257
2258 if (R < 0) {
2259 R = 0;
2260 }
2261
2262 score = R;
2263 mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
2264 } else {
2265 target.jitter = 0;
2266 target.jitter_min = 0;
2267 target.jitter_max = 0;
2268 mos = 0;
2269 }
2270
2271 /* Check which mode is on and do the warn / Crit stuff */
2272 if (modes.rta_mode) {
2273 mp_subcheck sc_rta = mp_subcheck_init();
2274 sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK);
2275 xasprintf(&sc_rta.output, "rta %0.3fms", (double)rta / 1000);
2276
2277 if (rta >= crit.rta) {
2278 sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL);
2279 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)crit.rta / 1000);
2280 } else if (rta >= warn.rta) {
2281 sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING);
2282 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)warn.rta / 1000);
2283 }
2284
2285 if (packet_loss < 100) {
2286 mp_perfdata pd_rta = perfdata_init();
2287 xasprintf(&pd_rta.label, "%srta", address);
2288 pd_rta.uom = strdup("ms");
2289 pd_rta.value = mp_create_pd_value(rta / 1000);
2290 pd_rta.min = mp_create_pd_value(0);
2291
2292 pd_rta.warn = mp_range_set_end(pd_rta.warn, mp_create_pd_value(warn.rta));
2293 pd_rta.crit = mp_range_set_end(pd_rta.crit, mp_create_pd_value(crit.rta));
2294 mp_add_perfdata_to_subcheck(&sc_rta, pd_rta);
2295
2296 mp_perfdata pd_rt_min = perfdata_init();
2297 xasprintf(&pd_rt_min.label, "%srtmin", address);
2298 pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000);
2299 pd_rt_min.uom = strdup("ms");
2300 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min);
2301
2302 mp_perfdata pd_rt_max = perfdata_init();
2303 xasprintf(&pd_rt_max.label, "%srtmax", address);
2304 pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000);
2305 pd_rt_max.uom = strdup("ms");
2306 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max);
2307 }
2308
2309 mp_add_subcheck_to_subcheck(&result, sc_rta);
2310 }
2311
2312 if (modes.pl_mode) {
2313 mp_subcheck sc_pl = mp_subcheck_init();
2314 sc_pl = mp_set_subcheck_default_state(sc_pl, STATE_OK);
2315 xasprintf(&sc_pl.output, "packet loss %.1f%%", packet_loss);
2316
2317 if (packet_loss >= crit.pl) {
2318 sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL);
2319 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, crit.pl);
2320 } else if (packet_loss >= warn.pl) {
2321 sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING);
2322 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, warn.pl);
2323 }
2324
2325 mp_perfdata pd_pl = perfdata_init();
2326 xasprintf(&pd_pl.label, "%spl", address);
2327 pd_pl.uom = strdup("%");
2328
2329 pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl));
2330 pd_pl.crit = mp_range_set_end(pd_pl.crit, mp_create_pd_value(crit.pl));
2331 pd_pl.value = mp_create_pd_value(packet_loss);
2332
2333 mp_add_perfdata_to_subcheck(&sc_pl, pd_pl);
2334
2335 mp_add_subcheck_to_subcheck(&result, sc_pl);
2336 }
2337
2338 if (modes.jitter_mode) {
2339 mp_subcheck sc_jitter = mp_subcheck_init();
2340 sc_jitter = mp_set_subcheck_default_state(sc_jitter, STATE_OK);
2341 xasprintf(&sc_jitter.output, "jitter %0.3fms", target.jitter);
2342
2343 if (target.jitter >= crit.jitter) {
2344 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL);
2345 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, crit.jitter);
2346 } else if (target.jitter >= warn.jitter) {
2347 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING);
2348 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, warn.jitter);
2349 }
2350
2351 if (packet_loss < 100) {
2352 mp_perfdata pd_jitter = perfdata_init();
2353 pd_jitter.uom = strdup("ms");
2354 xasprintf(&pd_jitter.label, "%sjitter_avg", address);
2355 pd_jitter.value = mp_create_pd_value(target.jitter);
2356 pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter));
2357 pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter));
2358 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
2359
2360 mp_perfdata pd_jitter_min = perfdata_init();
2361 pd_jitter_min.uom = strdup("ms");
2362 xasprintf(&pd_jitter_min.label, "%sjitter_min", address);
2363 pd_jitter_min.value = mp_create_pd_value(target.jitter_min);
2364 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min);
2365
2366 mp_perfdata pd_jitter_max = perfdata_init();
2367 pd_jitter_max.uom = strdup("ms");
2368 xasprintf(&pd_jitter_max.label, "%sjitter_max", address);
2369 pd_jitter_max.value = mp_create_pd_value(target.jitter_max);
2370 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max);
2371 }
2372 mp_add_subcheck_to_subcheck(&result, sc_jitter);
2373 }
2374
2375 if (modes.mos_mode) {
2376 mp_subcheck sc_mos = mp_subcheck_init();
2377 sc_mos = mp_set_subcheck_default_state(sc_mos, STATE_OK);
2378 xasprintf(&sc_mos.output, "MOS %0.1f", mos);
2379
2380 if (mos <= crit.mos) {
2381 sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL);
2382 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, crit.mos);
2383 } else if (mos <= warn.mos) {
2384 sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING);
2385 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, warn.mos);
2386 }
2387
2388 if (packet_loss < 100) {
2389 mp_perfdata pd_mos = perfdata_init();
2390 xasprintf(&pd_mos.label, "%smos", address);
2391 pd_mos.value = mp_create_pd_value(mos);
2392 pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos));
2393 pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos));
2394 pd_mos.min = mp_create_pd_value(0); // MOS starts at 0
2395 pd_mos.max = mp_create_pd_value(5); // MOS max is 5, by definition
2396 mp_add_perfdata_to_subcheck(&sc_mos, pd_mos);
2397 }
2398 mp_add_subcheck_to_subcheck(&result, sc_mos);
2399 }
2400
2401 if (modes.score_mode) {
2402 mp_subcheck sc_score = mp_subcheck_init();
2403 sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK);
2404 xasprintf(&sc_score.output, "Score %f", score);
2405
2406 if (score <= crit.score) {
2407 sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL);
2408 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, crit.score);
2409 } else if (score <= warn.score) {
2410 sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING);
2411 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, warn.score);
2412 }
2413
2414 if (packet_loss < 100) {
2415 mp_perfdata pd_score = perfdata_init();
2416 xasprintf(&pd_score.label, "%sscore", address);
2417 pd_score.value = mp_create_pd_value(score);
2418 pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score));
2419 pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score));
2420 pd_score.min = mp_create_pd_value(0);
2421 pd_score.max = mp_create_pd_value(100);
2422 mp_add_perfdata_to_subcheck(&sc_score, pd_score);
2423 }
2424
2425 mp_add_subcheck_to_subcheck(&result, sc_score);
2426 }
2427
2428 if (modes.order_mode) {
2429 mp_subcheck sc_order = mp_subcheck_init();
2430 sc_order = mp_set_subcheck_default_state(sc_order, STATE_OK);
2431
2432 if (target.found_out_of_order_packets) {
2433 mp_set_subcheck_state(sc_order, STATE_CRITICAL);
2434 xasprintf(&sc_order.output, "Packets out of order");
2435 } else {
2436 xasprintf(&sc_order.output, "Packets in order");
2437 }
2438
2439 mp_add_subcheck_to_subcheck(&result, sc_order);
2440 }
2441
2442 return result;
2443}
2444
2445evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
2446 check_icmp_mode_switches modes, check_icmp_threshold warn,
2447 check_icmp_threshold crit) {
2448 evaluate_host_wrapper result = {
2449 .targets_warn = 0,
2450 .targets_ok = 0,
2451 .sc_host = mp_subcheck_init(),
2452 };
2453 result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK);
2454
2455 result.sc_host.output = strdup(host.name);
2456
2457 ping_target *target = host.target_list;
2458 for (unsigned int i = 0; i < host.number_of_targets; i++) {
2459 mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit);
2460
2461 mp_state_enum target_state = mp_compute_subcheck_state(sc_target);
2462
2463 if (target_state == STATE_WARNING) {
2464 result.targets_warn++;
2465 } else if (target_state == STATE_OK) {
2466 result.targets_ok++;
2467 }
2468 mp_add_subcheck_to_subcheck(&result.sc_host, sc_target);
2469
2470 target = target->next;
2471 }
2472
2473 return result;
2196} 2474}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c
new file mode 100644
index 00000000..1b96a392
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.c
@@ -0,0 +1,134 @@
1#include "./config.h"
2#include <math.h>
3#include <netinet/in.h>
4#include <sys/socket.h>
5#include "./check_icmp_helpers.h"
6#include "../../plugins/netutils.h"
7
8// timeout as a global variable to make it available to the timeout handler
9unsigned int timeout = DEFAULT_TIMEOUT;
10
11check_icmp_config check_icmp_config_init() {
12 check_icmp_config tmp = {
13 .modes =
14 {
15 .order_mode = false,
16 .mos_mode = false,
17 .rta_mode = false,
18 .pl_mode = false,
19 .jitter_mode = false,
20 .score_mode = false,
21 },
22
23 .min_hosts_alive = -1,
24 .crit = {.pl = DEFAULT_CRIT_PL,
25 .rta = DEFAULT_CRIT_RTA,
26 .jitter = 50.0,
27 .mos = 3.0,
28 .score = 70.0},
29 .warn = {.pl = DEFAULT_WARN_PL,
30 .rta = DEFAULT_WARN_RTA,
31 .jitter = 40.0,
32 .mos = 3.5,
33 .score = 80.0},
34
35 .ttl = DEFAULT_TTL,
36 .icmp_data_size = DEFAULT_PING_DATA_SIZE,
37 .target_interval = 0,
38 .number_of_packets = DEFAULT_NUMBER_OF_PACKETS,
39
40 .source_ip = NULL,
41 .need_v4 = false,
42 .need_v6 = false,
43
44 .sender_id = 0,
45
46 .mode = MODE_RTA,
47
48 .number_of_targets = 0,
49 .targets = NULL,
50
51 .number_of_hosts = 0,
52 .hosts = NULL,
53
54 .output_format_is_set = false,
55 };
56 return tmp;
57}
58
59ping_target ping_target_init() {
60 ping_target tmp = {
61 .rtmin = INFINITY,
62
63 .jitter_min = INFINITY,
64
65 .found_out_of_order_packets = false,
66 };
67
68 return tmp;
69}
70
71check_icmp_state check_icmp_state_init() {
72 check_icmp_state tmp = {.icmp_sent = 0, .icmp_lost = 0, .icmp_recv = 0, .targets_down = 0};
73
74 return tmp;
75}
76
77ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) {
78 ping_target_create_wrapper result = {
79 .errorcode = 0,
80 };
81
82 struct sockaddr_storage *tmp_addr = &address;
83
84 /* disregard obviously stupid addresses
85 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
86 if (((tmp_addr->ss_family == AF_INET &&
87 (((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_NONE ||
88 ((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) ||
89 (tmp_addr->ss_family == AF_INET6 &&
90 (((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
91 result.errorcode = 1;
92 return result;
93 }
94
95 /* add the fresh ip */
96 ping_target target = ping_target_init();
97
98 /* fill out the sockaddr_storage struct */
99 target.address = address;
100
101 result.host = target;
102
103 return result;
104}
105
106check_icmp_target_container check_icmp_target_container_init() {
107 check_icmp_target_container tmp = {
108 .name = NULL,
109 .number_of_targets = 0,
110 .target_list = NULL,
111 };
112 return tmp;
113}
114
115unsigned int ping_target_list_append(ping_target *list, ping_target *elem) {
116 if (elem == NULL || list == NULL) {
117 return 0;
118 }
119
120 while (list->next != NULL) {
121 list = list->next;
122 }
123
124 list->next = elem;
125
126 unsigned int result = 1;
127
128 while (elem->next != NULL) {
129 result++;
130 elem = elem->next;
131 }
132
133 return result;
134}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h
new file mode 100644
index 00000000..dc6ea40b
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.h
@@ -0,0 +1,68 @@
1#pragma once
2
3#include "../../lib/states.h"
4#include <netinet/in_systm.h>
5#include <netinet/in.h>
6#include <netinet/ip.h>
7#include <netinet/ip6.h>
8#include <netinet/ip_icmp.h>
9#include <netinet/icmp6.h>
10#include <arpa/inet.h>
11
12typedef struct ping_target {
13 unsigned short id; /* id in **table, and icmp pkts */
14 char *msg; /* icmp error message, if any */
15
16 struct sockaddr_storage address; /* the address of this host */
17 struct sockaddr_storage error_addr; /* stores address of error replies */
18 time_t time_waited; /* total time waited, in usecs */
19 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
20 unsigned char icmp_type, icmp_code; /* type and code from errors */
21 unsigned short flags; /* control/status flags */
22
23 double rtmax; /* max rtt */
24 double rtmin; /* min rtt */
25
26 double jitter; /* measured jitter */
27 double jitter_max; /* jitter rtt maximum */
28 double jitter_min; /* jitter rtt minimum */
29
30 time_t last_tdiff;
31 unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
32
33 bool found_out_of_order_packets;
34
35 struct ping_target *next;
36} ping_target;
37
38ping_target ping_target_init();
39
40typedef struct {
41 char *name;
42 ping_target *target_list;
43 unsigned int number_of_targets;
44} check_icmp_target_container;
45
46check_icmp_target_container check_icmp_target_container_init();
47
48typedef struct {
49 unsigned int icmp_sent;
50 unsigned int icmp_recv;
51 unsigned int icmp_lost;
52 unsigned short targets_down;
53} check_icmp_state;
54
55check_icmp_state check_icmp_state_init();
56
57typedef struct {
58 int errorcode;
59 ping_target host;
60} ping_target_create_wrapper;
61
62typedef struct {
63 int socket4;
64 int socket6;
65} check_icmp_socket_set;
66
67ping_target_create_wrapper ping_target_create(struct sockaddr_storage address);
68unsigned int ping_target_list_append(ping_target *list, ping_target *elem);
diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h
new file mode 100644
index 00000000..be388d0a
--- /dev/null
+++ b/plugins-root/check_icmp.d/config.h
@@ -0,0 +1,115 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/states.h"
5#include <stddef.h>
6#include <netinet/in_systm.h>
7#include <netinet/in.h>
8#include <netinet/ip.h>
9#include <netinet/ip6.h>
10#include <netinet/ip_icmp.h>
11#include <netinet/icmp6.h>
12#include <arpa/inet.h>
13#include <stdint.h>
14#include "./check_icmp_helpers.h"
15#include "output.h"
16
17/* threshold structure. all values are maximum allowed, exclusive */
18typedef struct {
19 unsigned char pl; /* max allowed packet loss in percent */
20 time_t rta; /* roundtrip time average, microseconds */
21 double jitter; /* jitter time average, microseconds */
22 double mos; /* MOS */
23 double score; /* Score */
24} check_icmp_threshold;
25
26/* the different modes of this program are as follows:
27 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
28 * MODE_HOSTCHECK: Return immediately upon any sign of life
29 * In addition, sends packets to ALL addresses assigned
30 * to this host (as returned by gethostbyname() or
31 * gethostbyaddr() and expects one host only to be checked at
32 * a time. Therefore, any packet response what so ever will
33 * count as a sign of life, even when received outside
34 * crit.rta limit. Do not misspell any additional IP's.
35 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
36 * MODE_ICMP: Default Mode
37 */
38typedef enum {
39 MODE_RTA,
40 MODE_HOSTCHECK,
41 MODE_ALL,
42 MODE_ICMP,
43} check_icmp_execution_mode;
44
45typedef struct {
46 bool order_mode;
47 bool mos_mode;
48 bool rta_mode;
49 bool pl_mode;
50 bool jitter_mode;
51 bool score_mode;
52} check_icmp_mode_switches;
53
54typedef struct {
55 check_icmp_mode_switches modes;
56
57 int min_hosts_alive;
58 check_icmp_threshold crit;
59 check_icmp_threshold warn;
60
61 unsigned long ttl;
62 unsigned short icmp_data_size;
63 time_t target_interval;
64 unsigned short number_of_packets;
65
66 char *source_ip;
67 bool need_v4;
68 bool need_v6;
69
70 uint16_t sender_id; // PID of the main process, which is used as an ID in packets
71
72 check_icmp_execution_mode mode;
73
74 unsigned short number_of_targets;
75 ping_target *targets;
76
77 unsigned short number_of_hosts;
78 check_icmp_target_container *hosts;
79
80 mp_output_format output_format;
81 bool output_format_is_set;
82} check_icmp_config;
83
84check_icmp_config check_icmp_config_init();
85
86/* the data structure */
87typedef struct icmp_ping_data {
88 struct timeval stime; /* timestamp (saved in protocol struct as well) */
89 unsigned short ping_id;
90} icmp_ping_data;
91
92#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
93#define IP_HDR_SIZE 20
94#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
95#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
96#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
97
98/* 80 msec packet interval by default */
99// DEPRECATED, remove when removing the option
100#define DEFAULT_PKT_INTERVAL 80000
101
102#define DEFAULT_TARGET_INTERVAL 0
103
104#define DEFAULT_WARN_RTA 200000
105#define DEFAULT_CRIT_RTA 500000
106#define DEFAULT_WARN_PL 40
107#define DEFAULT_CRIT_PL 80
108
109#define DEFAULT_TIMEOUT 10
110#define DEFAULT_TTL 64
111
112#define DEFAULT_NUMBER_OF_PACKETS 5
113
114#define PACKET_BACKOFF_FACTOR 1.5
115#define TARGET_BACKOFF_FACTOR 1.5
diff --git a/plugins-root/pst3.c b/plugins-root/pst3.c
index 1f69f3a6..1bfe3d35 100644
--- a/plugins-root/pst3.c
+++ b/plugins-root/pst3.c
@@ -1,45 +1,45 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* pst3 3 * pst3
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2008 Monitoring Plugins Development Team 6 * Copyright (c) 2008 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the pst3 executable. This is a replacement ps command 10 * This file contains the pst3 executable. This is a replacement ps command
11* for Solaris to get output which provides a long argument listing, which 11 * for Solaris to get output which provides a long argument listing, which
12* is not possible with the standard ps command (due to truncation). /usr/ucb/ps 12 * is not possible with the standard ps command (due to truncation). /usr/ucb/ps
13* also has issues where some fields run into each other. 13 * also has issues where some fields run into each other.
14* 14 *
15* This executable works by reading process address structures, so needs 15 * This executable works by reading process address structures, so needs
16* to be executed as root 16 * to be executed as root
17* 17 *
18* Originally written by R.W.Ingraham 18 * Originally written by R.W.Ingraham
19* Rewritten by Duncan Ferguson (Altinity Ltd, June 2008) 19 * Rewritten by Duncan Ferguson (Altinity Ltd, June 2008)
20* The rewrite was necessary as /dev/kmem is not available within 20 * The rewrite was necessary as /dev/kmem is not available within
21* non-global zones on Solaris 10 21 * non-global zones on Solaris 10
22* 22 *
23* Details for rewrite came from 23 * Details for rewrite came from
24* source of /usr/ucb/ps on Solaris: 24 * source of /usr/ucb/ps on Solaris:
25* http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff 25 * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff
26* usenet group posting 26 * usenet group posting
27* http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F 27 * http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F
28* 28 *
29* This program is free software: you can redistribute it and/or modify 29 * This program is free software: you can redistribute it and/or modify
30* it under the terms of the GNU General Public License as published by 30 * it under the terms of the GNU General Public License as published by
31* the Free Software Foundation, either version 3 of the License, or 31 * the Free Software Foundation, either version 3 of the License, or
32* (at your option) any later version. 32 * (at your option) any later version.
33* 33 *
34* This program is distributed in the hope that it will be useful, 34 * This program is distributed in the hope that it will be useful,
35* but WITHOUT ANY WARRANTY; without even the implied warranty of 35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37* GNU General Public License for more details. 37 * GNU General Public License for more details.
38* 38 *
39* You should have received a copy of the GNU General Public License 39 * You should have received a copy of the GNU General Public License
40* along with this program. If not, see <http://www.gnu.org/licenses/>. 40 * along with this program. If not, see <http://www.gnu.org/licenses/>.
41* 41 *
42*****************************************************************************/ 42 *****************************************************************************/
43 43
44#include <stdio.h> 44#include <stdio.h>
45#include <stdlib.h> 45#include <stdlib.h>
@@ -55,14 +55,14 @@
55 * Constants 55 * Constants
56 */ 56 */
57 57
58#define PROC_DIR "/proc" 58#define PROC_DIR "/proc"
59#define ARGS 30 59#define ARGS 30
60 60
61/* 61/*
62 * Globals 62 * Globals
63 */ 63 */
64 64
65static char * szProg; 65static char *szProg;
66 66
67/* 67/*
68 * Prototypes 68 * Prototypes
@@ -71,192 +71,179 @@ void usage();
71 71
72/*----------------------------------------------------------------------------*/ 72/*----------------------------------------------------------------------------*/
73 73
74int main (int argc, char **argv) 74int main(int argc, char **argv) {
75{ 75 DIR *procdir;
76 DIR *procdir; 76 struct dirent *proc;
77 struct dirent *proc; 77 char ps_name[ARGS];
78 char ps_name[ARGS]; 78 char as_name[ARGS];
79 char as_name[ARGS]; 79 psinfo_t psinfo;
80 psinfo_t psinfo; 80
81 81 /* Set our program name global */
82 /* Set our program name global */ 82 if ((szProg = strrchr(argv[0], '/')) != NULL) {
83 if ((szProg = strrchr(argv[0], '/')) != NULL) 83 szProg++;
84 szProg++; 84 } else {
85 else 85 szProg = argv[0];
86 szProg = argv[0]; 86 }
87 87
88 /* if given any parameters, print out help */ 88 /* if given any parameters, print out help */
89 if(argc > 1) { 89 if (argc > 1) {
90 (void)usage(); 90 (void)usage();
91 exit(1); 91 exit(1);
92 } 92 }
93 93
94 /* Make sure that our euid is root */ 94 /* Make sure that our euid is root */
95 if (geteuid() != 0) 95 if (geteuid() != 0) {
96 { 96 fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg);
97 fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg); 97 exit(1);
98 exit(1); 98 }
99 } 99
100 100 if ((procdir = opendir(PROC_DIR)) == NULL) {
101 if ((procdir = opendir(PROC_DIR)) == NULL) { 101 fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR);
102 fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR); 102 exit(1);
103 exit(1); 103 }
104 } 104
105 105 /* Display column headings */
106 /* Display column headings */ 106 printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", 'S', "UID", "PID", "PPID", "VSZ", "RSS", "%CPU",
107 printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", 107 "COMMAND", "ARGS");
108 'S', 108
109 "UID", 109 /* Zip through all of the process entries */
110 "PID", 110 while ((proc = readdir(procdir))) {
111 "PPID", 111 int ps_fd;
112 "VSZ", 112 int as_fd;
113 "RSS", 113 off_t argoff;
114 "%CPU", 114 int i;
115 "COMMAND", 115 char *args;
116 "ARGS" 116 char *procname;
117 ); 117 char *ptr;
118 118 int argslen;
119 /* Zip through all of the process entries */ 119 uintptr_t args_addr;
120 while((proc = readdir(procdir))) { 120 ;
121 int ps_fd; 121 uintptr_t *args_vecs;
122 int as_fd; 122 ;
123 off_t argoff; 123 int args_count;
124 int i; 124
125 char *args; 125 if (proc->d_name[0] == '.') {
126 char *procname; 126 continue;
127 char *ptr; 127 }
128 int argslen; 128
129 uintptr_t args_addr;; 129 sprintf(ps_name, "%s/%s/%s", PROC_DIR, proc->d_name, "psinfo");
130 uintptr_t *args_vecs;; 130 sprintf(as_name, "%s/%s/%s", PROC_DIR, proc->d_name, "as");
131 int args_count; 131 try_again:
132 132 if ((ps_fd = open(ps_name, O_RDONLY)) == -1) {
133 if(proc->d_name[0] == '.') 133 continue;
134 continue; 134 }
135 135
136 sprintf(ps_name,"%s/%s/%s",PROC_DIR,proc->d_name,"psinfo"); 136 if ((as_fd = open(as_name, O_RDONLY)) == -1) {
137 sprintf(as_name,"%s/%s/%s",PROC_DIR,proc->d_name,"as"); 137 close(ps_fd);
138try_again: 138 continue;
139 if((ps_fd = open(ps_name, O_RDONLY)) == -1) 139 }
140 continue; 140
141 141 if (read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
142 if((as_fd = open(as_name, O_RDONLY)) == -1) { 142 int err = errno;
143 close(ps_fd); 143 close(ps_fd);
144 continue; 144 close(as_fd);
145 } 145 if (err == EAGAIN) {
146 146 goto try_again;
147 if(read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { 147 }
148 int err = errno; 148 if (err != ENOENT) {
149 close(ps_fd); 149 fprintf(stderr, "%s: read() on %s: %s\n", szProg, ps_name, strerror(err));
150 close(as_fd); 150 }
151 if(err == EAGAIN) goto try_again; 151 continue;
152 if(err != ENOENT) 152 }
153 fprintf(stderr, "%s: read() on %s: %s\n", szProg, 153 close(ps_fd);
154 ps_name, strerror(err)); 154
155 continue; 155 /* system process, ignore since the previous version did */
156 } 156 if (psinfo.pr_nlwp == 0 || strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0) {
157 close(ps_fd); 157 continue;
158 158 }
159 /* system process, ignore since the previous version did */ 159
160 if( 160 /* get the procname to match previous versions */
161 psinfo.pr_nlwp == 0 || 161 procname = strdup(psinfo.pr_psargs);
162 strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0 162 if ((ptr = strchr(procname, ' ')) != NULL) {
163 ) { 163 *ptr = '\0';
164 continue; 164 }
165 } 165 if ((ptr = strrchr(procname, '/')) != NULL) {
166 166 ptr++;
167 /* get the procname to match previous versions */ 167 } else {
168 procname = strdup(psinfo.pr_psargs); 168 ptr = procname;
169 if((ptr = strchr(procname, ' ')) != NULL) 169 }
170 *ptr = '\0'; 170
171 if((ptr = strrchr(procname, '/')) != NULL) 171 /*
172 ptr++; 172 * print out what we currently know
173 else 173 */
174 ptr = procname; 174 printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", psinfo.pr_lwp.pr_sname, psinfo.pr_euid,
175 175 psinfo.pr_pid, psinfo.pr_ppid, psinfo.pr_size, psinfo.pr_rssize,
176 /* 176 ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), ptr);
177 * print out what we currently know 177 free(procname);
178 */ 178
179 printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", 179 /*
180 psinfo.pr_lwp.pr_sname, 180 * and now for the command line stuff
181 psinfo.pr_euid, 181 */
182 psinfo.pr_pid, 182
183 psinfo.pr_ppid, 183 args_addr = psinfo.pr_argv;
184 psinfo.pr_size, 184 args_count = psinfo.pr_argc;
185 psinfo.pr_rssize, 185 args_vecs = malloc(args_count * sizeof(uintptr_t));
186 ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), 186
187 ptr 187 if (psinfo.pr_dmodel == PR_MODEL_NATIVE) {
188 ); 188 /* this process matches target process */
189 free(procname); 189 pread(as_fd, args_vecs, args_count * sizeof(uintptr_t), args_addr);
190 190 } else {
191 /* 191 /* this process is 64bit, target process is 32 bit*/
192 * and now for the command line stuff 192 caddr32_t *args_vecs32 = (caddr32_t *)args_vecs;
193 */ 193 pread(as_fd, args_vecs32, args_count * sizeof(caddr32_t), args_addr);
194 194 for (i = args_count - 1; i >= 0; --i) {
195 args_addr = psinfo.pr_argv; 195 args_vecs[i] = args_vecs32[i];
196 args_count = psinfo.pr_argc; 196 }
197 args_vecs = malloc(args_count * sizeof(uintptr_t)); 197 }
198 198
199 if(psinfo.pr_dmodel == PR_MODEL_NATIVE) { 199 /*
200 /* this process matches target process */ 200 * now read in the args - if what we read in fills buffer
201 pread(as_fd,args_vecs, args_count * sizeof(uintptr_t), 201 * resize buffer and reread that bit again
202 args_addr); 202 */
203 } else { 203 argslen = ARGS;
204 /* this process is 64bit, target process is 32 bit*/ 204 args = malloc(argslen + 1);
205 caddr32_t *args_vecs32 = (caddr32_t *)args_vecs; 205 for (i = 0; i < args_count; i++) {
206 pread(as_fd,args_vecs32,args_count * sizeof(caddr32_t), 206 memset(args, '\0', argslen + 1);
207 args_addr); 207 if (pread(as_fd, args, argslen, args_vecs[i]) <= 0) {
208 for (i=args_count-1;i>=0;--i) 208 break;
209 args_vecs[i]=args_vecs32[i]; 209 }
210 } 210 args[argslen] = '\0';
211 211 if (strlen(args) == argslen) {
212 /* 212 argslen += ARGS;
213 * now read in the args - if what we read in fills buffer 213 args = realloc(args, argslen + 1);
214 * resize buffer and reread that bit again 214 i--;
215 */ 215 continue;
216 argslen=ARGS; 216 }
217 args=malloc(argslen+1); 217 printf(" %s", args);
218 for(i=0;i<args_count;i++) { 218 }
219 memset(args,'\0',argslen+1); 219 free(args_vecs);
220 if(pread(as_fd, args, argslen, args_vecs[i]) <= 0) { 220 free(args);
221 break; 221 close(as_fd);
222 } 222 printf("\n");
223 args[argslen]='\0'; 223 }
224 if(strlen(args) == argslen){ 224
225 argslen += ARGS; 225 (void)closedir(procdir);
226 args = realloc(args, argslen + 1); 226
227 i--; 227 return (0);
228 continue;
229 }
230 printf(" %s", args);
231 }
232 free(args_vecs);
233 free(args);
234 close(as_fd);
235 printf("\n");
236 }
237
238 (void) closedir(procdir);
239
240 return (0);
241} 228}
242 229
243/*----------------------------------------------------------------------------*/ 230/*----------------------------------------------------------------------------*/
244 231
245void usage() { 232void usage() {
246 printf("%s: Help output\n\n", szProg); 233 printf("%s: Help output\n\n", szProg);
247 printf("If this program is given any arguments, this help is displayed.\n"); 234 printf("If this program is given any arguments, this help is displayed.\n");
248 printf("This command is used to print out the full command line for all\n"); 235 printf("This command is used to print out the full command line for all\n");
249 printf("running processes because /usr/bin/ps is limited to 80 chars and\n"); 236 printf("running processes because /usr/bin/ps is limited to 80 chars and\n");
250 printf("/usr/ucb/ps can merge columns together.\n\n"); 237 printf("/usr/ucb/ps can merge columns together.\n\n");
251 printf("Columns are:\n"); 238 printf("Columns are:\n");
252 printf("\tS - State of process - see 'ps' man page\n"); 239 printf("\tS - State of process - see 'ps' man page\n");
253 printf("\tUID - UID of the process owner\n"); 240 printf("\tUID - UID of the process owner\n");
254 printf("\tPID - PID of the process\n"); 241 printf("\tPID - PID of the process\n");
255 printf("\tPPID - PID of the parent process\n"); 242 printf("\tPPID - PID of the parent process\n");
256 printf("\tVSZ - Virtual memory usage (kilobytes)\n"); 243 printf("\tVSZ - Virtual memory usage (kilobytes)\n");
257 printf("\tRSS - Real memory usage (kilobytes)\n"); 244 printf("\tRSS - Real memory usage (kilobytes)\n");
258 printf("\t%%CPU - CPU usage\n"); 245 printf("\t%%CPU - CPU usage\n");
259 printf("\tCOMMAND - Command being run\n"); 246 printf("\tCOMMAND - Command being run\n");
260 printf("\tARGS - Full command line with arguments\n"); 247 printf("\tARGS - Full command line with arguments\n");
261 return; 248 return;
262} 249}
diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t
index ce627736..70392154 100644
--- a/plugins-root/t/check_dhcp.t
+++ b/plugins-root/t/check_dhcp.t
@@ -12,14 +12,14 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 6; 15 plan tests => 7;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_dhcp"; 17 plan skip_all => "Need sudo to test check_dhcp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK: Received \d+ DHCPOFFER\(s\), \d+ of 1 requested servers responded, max lease time = \d+ sec\./'; 21my $successOutput = '/Received \d+ DHCPOFFER(s)*, max lease time = \d+ seconds/';
22my $failureOutput = '/CRITICAL: (No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; 22my $failureOutput = '/(No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/';
23my $invalidOutput = '/Invalid hostname/'; 23my $invalidOutput = '/Invalid hostname/';
24 24
25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE", 25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE",
@@ -34,6 +34,8 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
34 "An invalid (not known to DNS) hostname", 34 "An invalid (not known to DNS) hostname",
35 "nosuchhost" ); 35 "nosuchhost" );
36 36
37my $output_format = "--output-format mp-test-json";
38
37# try to determince interface 39# try to determince interface
38my $interface = ''; 40my $interface = '';
39 41
@@ -49,19 +51,21 @@ my $res;
49SKIP: { 51SKIP: {
50 skip('need responsive test host', 2) unless $host_responsive; 52 skip('need responsive test host', 2) unless $host_responsive;
51 $res = NPTest->testCmd( 53 $res = NPTest->testCmd(
52 "$sudo ./check_dhcp $interface -u -s $host_responsive" 54 "$sudo ./check_dhcp $interface -u -s $host_responsive $output_format"
53 ); 55 );
54 is( $res->return_code, 0, "Syntax ok" ); 56 is( $res->return_code, 0, "with JSON test format result should always be OK" );
55 like( $res->output, $successOutput, "Output OK" ); 57 like( $res->{'mp_test_result'}->{'state'}, "/OK/", "Output OK" );
58 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $successOutput, "Output OK" );
56}; 59};
57 60
58SKIP: { 61SKIP: {
59 skip('need nonresponsive test host', 2) unless $host_nonresponsive; 62 skip('need nonresponsive test host', 2) unless $host_nonresponsive;
60 $res = NPTest->testCmd( 63 $res = NPTest->testCmd(
61 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive" 64 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive $output_format"
62 ); 65 );
63 is( $res->return_code, 2, "Exit code - host nonresponsive" ); 66 is( $res->return_code, 0, "with JSON test format result should always be OK" );
64 like( $res->output, $failureOutput, "Output OK" ); 67 like( $res->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Exit code - host nonresponsive" );
68 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $failureOutput, "Output OK" );
65}; 69};
66 70
67SKIP: { 71SKIP: {
@@ -69,6 +73,6 @@ SKIP: {
69 $res = NPTest->testCmd( 73 $res = NPTest->testCmd(
70 "$sudo ./check_dhcp $interface -u -s $hostname_invalid" 74 "$sudo ./check_dhcp $interface -u -s $hostname_invalid"
71 ); 75 );
72 is( $res->return_code, 3, "Exit code - host invalid" ); 76 is( $res->return_code, 3, "invalid hostname/address should return UNKNOWN" );
73 like( $res->output, $invalidOutput, "Output OK" ); 77 like( $res->output, $invalidOutput, "Output OK" );
74}; 78};
diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t
index de1d88d2..d414c3c7 100644
--- a/plugins-root/t/check_icmp.t
+++ b/plugins-root/t/check_icmp.t
@@ -12,15 +12,12 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 40; 15 plan tests => 17;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_icmp"; 17 plan skip_all => "Need sudo to test check_icmp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK - .*? rta (?:[\d\.]+ms)|(?:nan), lost \d+%/';
22my $failureOutput = '/(WARNING|CRITICAL) - .*? rta (?:[\d\.]+ms > [\d\.]+ms|nan)/';
23
24my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE", 21my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE",
25 "The hostname of system responsive to network requests", 22 "The hostname of system responsive to network requests",
26 "localhost" ); 23 "localhost" );
@@ -36,108 +33,85 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
36my $res; 33my $res;
37 34
38$res = NPTest->testCmd( 35$res = NPTest->testCmd(
39 "$sudo ./check_icmp -H $host_responsive -w 10000ms,100% -c 10000ms,100%" 36 "$sudo ./check_icmp -H $host_responsive -w 100ms,100% -c 100ms,100%"
40 ); 37 );
41is( $res->return_code, 0, "Syntax ok" ); 38is( $res->return_code, 0, "Syntax ok" );
42like( $res->output, $successOutput, "Output OK" );
43 39
44$res = NPTest->testCmd( 40$res = NPTest->testCmd(
45 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 10000ms,100%" 41 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 100ms,100%"
46 ); 42 );
47is( $res->return_code, 1, "Syntax ok, with forced warning" ); 43is( $res->return_code, 1, "Syntax ok, with forced warning" );
48like( $res->output, $failureOutput, "Output OK" );
49 44
50$res = NPTest->testCmd( 45$res = NPTest->testCmd(
51 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%" 46 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%"
52 ); 47 );
53is( $res->return_code, 2, "Syntax ok, with forced critical" ); 48is( $res->return_code, 2, "Syntax ok, with forced critical" );
54like( $res->output, $failureOutput, "Output OK" );
55 49
56$res = NPTest->testCmd( 50$res = NPTest->testCmd(
57 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -t 2" 51 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100%"
58 ); 52 );
59is( $res->return_code, 2, "Timeout - host nonresponsive" ); 53is( $res->return_code, 2, "Timeout - host nonresponsive" );
60like( $res->output, '/pl=100%/', "Error contains 'pl=100%' string (for 100% packet loss)" );
61like( $res->output, '/rta=U/', "Error contains 'rta=U' string" );
62 54
63$res = NPTest->testCmd( 55$res = NPTest->testCmd(
64 "$sudo ./check_icmp -w 10000ms,100% -c 10000ms,100%" 56 "$sudo ./check_icmp -w 100ms,100% -c 100ms,100%"
65 ); 57 );
66is( $res->return_code, 3, "No hostname" ); 58is( $res->return_code, 3, "No hostname" );
67like( $res->output, '/No hosts to check/', "Output with appropriate error message");
68 59
69$res = NPTest->testCmd( 60$res = NPTest->testCmd(
70 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 0 -t 2" 61 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 0"
71 ); 62 );
72is( $res->return_code, 0, "One host nonresponsive - zero required" ); 63is( $res->return_code, 0, "One host nonresponsive - zero required" );
73like( $res->output, $successOutput, "Output OK" );
74 64
75$res = NPTest->testCmd( 65$res = NPTest->testCmd(
76 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 1 -t 2" 66 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 1"
77 ); 67 );
78is( $res->return_code, 0, "One of two host nonresponsive - one required" ); 68is( $res->return_code, 0, "One of two host nonresponsive - one required" );
79like( $res->output, $successOutput, "Output OK" );
80 69
81$res = NPTest->testCmd( 70$res = NPTest->testCmd(
82 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 71 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 2"
83 ); 72 );
84is( $res->return_code, 2, "One of two host nonresponsive - two required" ); 73is( $res->return_code, 2, "One of two host nonresponsive - two required" );
85like( $res->output, $failureOutput, "Output OK" );
86 74
87$res = NPTest->testCmd( 75$res = NPTest->testCmd(
88 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 76 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1"
89 ); 77 );
90is( $res->return_code, 0, "IPv4 source_ip accepted" ); 78is( $res->return_code, 0, "IPv4 source_ip accepted" );
91like( $res->output, $successOutput, "Output OK" );
92 79
93$res = NPTest->testCmd( 80$res = NPTest->testCmd(
94 "$sudo ./check_icmp -H $host_responsive -b 65507" 81 "$sudo ./check_icmp -H $host_responsive -b 65507"
95 ); 82 );
96is( $res->return_code, 0, "Try max packet size" ); 83is( $res->return_code, 0, "Try max packet size" );
97like( $res->output, $successOutput, "Output OK - Didn't overflow" );
98 84
99$res = NPTest->testCmd( 85$res = NPTest->testCmd(
100 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1 -t 2" 86 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1"
101 ); 87 );
102is( $res->return_code, 0, "rta works" ); 88is( $res->return_code, 0, "rta works" );
103like( $res->output, $successOutput, "Output OK" );
104$res = NPTest->testCmd( 89$res = NPTest->testCmd(
105 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1 -t 2" 90 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1"
106 ); 91 );
107is( $res->return_code, 0, "pl works" ); 92is( $res->return_code, 0, "pl works" );
108like( $res->output, '/lost 0%/', "Output OK" );
109 93
110$res = NPTest->testCmd( 94$res = NPTest->testCmd(
111 "$sudo ./check_icmp -H $host_responsive -J 80,90 -t 2" 95 "$sudo ./check_icmp -H $host_responsive -J 80,90"
112 ); 96 );
113is( $res->return_code, 0, "jitter works" ); 97is( $res->return_code, 0, "jitter works" );
114like( $res->output, '/jitter \d/', "Output OK" );
115 98
116$res = NPTest->testCmd( 99$res = NPTest->testCmd(
117 "$sudo ./check_icmp -H $host_responsive -M 4,3 -t 2" 100 "$sudo ./check_icmp -H $host_responsive -M 4,3"
118 ); 101 );
119is( $res->return_code, 0, "mos works" ); 102is( $res->return_code, 0, "mos works" );
120like( $res->output, '/MOS \d/', "Output OK" );
121 103
122$res = NPTest->testCmd( 104$res = NPTest->testCmd(
123 "$sudo ./check_icmp -H $host_responsive -S 80,70 -t 2" 105 "$sudo ./check_icmp -H $host_responsive -S 80,70"
124 ); 106 );
125is( $res->return_code, 0, "score works" ); 107is( $res->return_code, 0, "score works" );
126like( $res->output, '/Score \d/', "Output OK" );
127 108
128$res = NPTest->testCmd( 109$res = NPTest->testCmd(
129 "$sudo ./check_icmp -H $host_responsive -O -t 2" 110 "$sudo ./check_icmp -H $host_responsive -O"
130 ); 111 );
131is( $res->return_code, 0, "order works" ); 112is( $res->return_code, 0, "order works" );
132like( $res->output, '/Packets in order/', "Output OK" );
133 113
134$res = NPTest->testCmd( 114$res = NPTest->testCmd(
135 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100 -t 2" 115 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100"
136 ); 116 );
137is( $res->return_code, 0, "order works" ); 117is( $res->return_code, 0, "order works" );
138like( $res->output, '/Packets in order/', "Output OK" );
139like( $res->output, '/Score \d/', "Output OK" );
140like( $res->output, '/MOS \d/', "Output OK" );
141like( $res->output, '/jitter \d/', "Output OK" );
142like( $res->output, '/lost 0%/', "Output OK" );
143like( $res->output, $successOutput, "Output OK" );
diff --git a/plugins-scripts/check_sensors.sh b/plugins-scripts/check_sensors.sh
index 866e0e0f..ba3581b1 100755..100644
--- a/plugins-scripts/check_sensors.sh
+++ b/plugins-scripts/check_sensors.sh
@@ -20,7 +20,6 @@ print_help() {
20 echo "This plugin checks hardware status using the lm_sensors package." 20 echo "This plugin checks hardware status using the lm_sensors package."
21 echo "" 21 echo ""
22 support 22 support
23 exit "$STATE_OK"
24} 23}
25 24
26case "$1" in 25case "$1" in
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index d43c1971..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,26 +33,70 @@ MATHLIBS = @MATHLIBS@
27#AM_CFLAGS = -Wall 33#AM_CFLAGS = -Wall
28 34
29libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ 35libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \
30 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_overcr check_ping \ 36 check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \
31 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ 37 check_real check_smtp check_ssh check_tcp check_time check_ntp_time \
32 check_ups check_users negate \ 38 check_ups check_users negate \
33 urlize @EXTRAS@ 39 urlize @EXTRAS@ \
40 check_snmp
34 41
35check_tcp_programs = check_ftp check_imap check_nntp check_pop \ 42check_tcp_programs = check_ftp check_imap check_nntp check_pop \
36 check_udp check_clamd @check_tcp_ssl@ 43 check_udp check_clamd @check_tcp_ssl@
37 44
38EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ 45EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \
39 check_swap check_fping check_ldap check_game check_dig \ 46 check_swap check_fping check_ldap check_game check_dig \
40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 47 check_nagios check_by_ssh check_dns check_nt check_ide_smart \
41 check_procs check_mysql_query check_apt check_dbi check_curl \ 48 check_procs check_mysql_query check_apt check_dbi check_curl \
42 \ 49 \
43 tests/test_check_swap 50 tests/test_check_swap \
51 tests/test_check_snmp \
52 tests/test_check_disk
44 53
45SUBDIRS = picohttpparser 54SUBDIRS = picohttpparser
46 55
47np_test_scripts = tests/test_check_swap.t 56np_test_scripts = tests/test_check_swap.t \
48 57 tests/test_check_snmp.t \
49EXTRA_DIST = t tests $(np_test_scripts) check_swap.d 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
50 100
51PLUGINHDRS = common.h 101PLUGINHDRS = common.h
52 102
@@ -85,9 +135,11 @@ check_cluster_LDADD = $(BASEOBJS)
85check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 135check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
86check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser 136check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser
87check_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
88check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 139check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
89check_dig_LDADD = $(NETLIBS) 140check_dig_LDADD = $(NETLIBS)
90check_disk_LDADD = $(BASEOBJS) 141check_disk_LDADD = $(BASEOBJS)
142check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c
91check_dns_LDADD = $(NETLIBS) 143check_dns_LDADD = $(NETLIBS)
92check_dummy_LDADD = $(BASEOBJS) 144check_dummy_LDADD = $(BASEOBJS)
93check_fping_LDADD = $(NETLIBS) 145check_fping_LDADD = $(NETLIBS)
@@ -108,14 +160,15 @@ check_nagios_LDADD = $(BASEOBJS)
108check_nt_LDADD = $(NETLIBS) 160check_nt_LDADD = $(NETLIBS)
109check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 161check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
110check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 162check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
111check_nwstat_LDADD = $(NETLIBS)
112check_overcr_LDADD = $(NETLIBS)
113check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) 163check_pgsql_LDADD = $(NETLIBS) $(PGLIBS)
114check_ping_LDADD = $(NETLIBS) 164check_ping_LDADD = $(NETLIBS)
115check_procs_LDADD = $(BASEOBJS) 165check_procs_LDADD = $(BASEOBJS)
116check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) 166check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
117check_real_LDADD = $(NETLIBS) 167check_real_LDADD = $(NETLIBS)
168check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
118check_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`
119check_smtp_LDADD = $(SSLOBJS) 172check_smtp_LDADD = $(SSLOBJS)
120check_ssh_LDADD = $(NETLIBS) 173check_ssh_LDADD = $(NETLIBS)
121check_swap_SOURCES = check_swap.c check_swap.d/swap.c 174check_swap_SOURCES = check_swap.c check_swap.d/swap.c
@@ -124,6 +177,7 @@ check_tcp_LDADD = $(SSLOBJS)
124check_time_LDADD = $(NETLIBS) 177check_time_LDADD = $(NETLIBS)
125check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) 178check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
126check_ups_LDADD = $(NETLIBS) 179check_ups_LDADD = $(NETLIBS)
180check_users_SOURCES = check_users.c check_users.d/users.c
127check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) 181check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
128check_by_ssh_LDADD = $(NETLIBS) 182check_by_ssh_LDADD = $(NETLIBS)
129check_ide_smart_LDADD = $(BASEOBJS) 183check_ide_smart_LDADD = $(BASEOBJS)
@@ -136,6 +190,10 @@ endif
136 190
137tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap 191tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
138tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c 192tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c
193tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
194tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c
195tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap
196tests_test_check_disk_SOURCES = tests/test_check_disk.c
139 197
140############################################################################## 198##############################################################################
141# secondary dependencies 199# secondary dependencies
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index 1eda45dd..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -29,92 +29,98 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "perfdata.h"
32const char *progname = "check_apt"; 33const char *progname = "check_apt";
33const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
34const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
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 {
43 UPGRADE,
44 DIST_UPGRADE,
45 NO_UPGRADE
46} upgrade_type;
47
48/* Character for hidden input file option (for testing). */
49#define INPUT_FILE_OPT CHAR_MAX + 1
50/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
51#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
52#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
53/* 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
54 * it here as well */ 50 * it here as well */
55#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
56# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
57#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
58/* 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 */
59#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
60/* the RE that catches security updates */ 57/* the RE that catches security updates */
61#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
62 59
63/* some standard functions */ 60/* some standard functions */
64static int process_arguments(int /*argc*/, char ** /*argv*/); 61typedef struct {
62 int errorcode;
63 check_apt_config config;
64} check_apt_config_wrapper;
65static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
65static void print_help(void); 66static void print_help(void);
66void print_usage(void); 67void print_usage(void);
67 68
68/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
69static char *construct_cmdline(upgrade_type u, const char *opts); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
70/* run an apt-get update */ 72/* run an apt-get update */
71static int 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);
72 79
73typedef struct { 80typedef struct {
74 int errorcode; 81 int errorcode;
75 int package_count; 82 size_t package_count;
76 int security_package_count; 83 size_t security_package_count;
77 char **packages_list; 84 char **packages_list;
78 char **secpackages_list; 85 char **secpackages_list;
86 bool exec_warning;
79} run_upgrade_result; 87} run_upgrade_result;
80 88
81/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
82static run_upgrade_result run_upgrade(void); 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);
83 93
84/* add another clause to a regexp */ 94/* add another clause to a regexp */
85static char *add_to_regexp(char *expr, const char *next); 95static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
86/* extract package name from Inst line */ 96/* extract package name from Inst line */
87static char *pkg_name(char *line); 97static char *pkg_name(char * /*line*/);
88/* string comparison function for qsort */ 98/* string comparison function for qsort */
89static int cmpstringp(const void *p1, const void *p2); 99static int cmpstringp(const void * /*p1*/, const void * /*p2*/);
90 100
91/* configuration variables */ 101/* configuration variables */
92static int verbose = 0; /* -v */ 102static int verbose = 0; /* -v */
93static bool list = false; /* list packages available for upgrade */
94static bool do_update = false; /* whether to call apt-get update */
95static bool only_critical = false; /* whether to warn about non-critical updates */
96static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */
97static char *upgrade_opts = NULL; /* options to override defaults for upgrade */
98static char *update_opts = NULL; /* options to override defaults for update */
99static char *do_include = NULL; /* regexp to only include certain packages */
100static char *do_exclude = NULL; /* regexp to only exclude certain packages */
101static char *do_critical = NULL; /* regexp specifying critical packages */
102static char *input_filename = NULL; /* input filename for testing */
103/* number of packages available for upgrade to return WARNING status */
104static int packages_warning = 1;
105 103
106/* other global variables */ 104/* other global variables */
107static int stderr_warning = 0; /* if a cmd issued output on stderr */ 105static bool stderr_warning = false; /* if a cmd issued output on stderr */
108static int exec_warning = 0; /* if a cmd exited non-zero */ 106static bool exec_warning = false; /* if a cmd exited non-zero */
109 107
110int main(int argc, char **argv) { 108int main(int argc, char **argv) {
111 /* Parse extra opts if any */ 109 /* Parse extra opts if any */
112 argv = np_extra_opts(&argc, argv, progname); 110 argv = np_extra_opts(&argc, argv, progname);
113 111
114 if (process_arguments(argc, argv) == ERROR) { 112 check_apt_config_wrapper tmp_config = process_arguments(argc, argv);
113
114 if (tmp_config.errorcode == ERROR) {
115 usage_va(_("Could not parse arguments")); 115 usage_va(_("Could not parse arguments"));
116 } 116 }
117 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 }
123
118 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
119 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
120 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
@@ -123,54 +129,91 @@ int main(int argc, char **argv) {
123 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
124 alarm(timeout_interval); 130 alarm(timeout_interval);
125 131
126 int result = STATE_UNKNOWN; 132 mp_check overall = mp_check_init();
127 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
128 if (do_update) { 134 if (config.do_update) {
129 result = run_update(); 135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
130 } 138 }
131 139
132 /* apt-get upgrade */ 140 /* apt-get upgrade */
133 run_upgrade_result upgrad_res = run_upgrade(); 141 run_upgrade_result upgrad_res =
142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
143 config.upgrade_opts, config.input_filename);
134 144
135 result = max_state(result, upgrad_res.errorcode); 145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
136 int packages_available = upgrad_res.package_count; 146 if (upgrad_res.errorcode == OK) {
137 int sec_count = upgrad_res.security_package_count; 147 sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK);
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;
138 char **packages_list = upgrad_res.packages_list; 155 char **packages_list = upgrad_res.packages_list;
139 char **secpackages_list = upgrad_res.secpackages_list; 156 char **secpackages_list = upgrad_res.secpackages_list;
140 157
141 if (sec_count > 0) { 158 mp_perfdata pd_security_updates = perfdata_init();
142 result = max_state(result, STATE_CRITICAL); 159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
143 } else if (packages_available >= packages_warning && only_critical == false) { 160 pd_security_updates.label = "critical_updates";
144 result = max_state(result, STATE_WARNING); 161
145 } else if (result > STATE_UNKNOWN) { 162 mp_subcheck sc_security_updates = mp_subcheck_init();
146 result = STATE_UNKNOWN; 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);
147 } 171 }
148 172
149 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 173 mp_perfdata pd_other_updates = perfdata_init();
150 state_text(result), packages_available, (upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, 174 pd_other_updates.value = mp_create_pd_value(packages_available);
151 (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", 175 pd_other_updates.label = "available_upgrades";
152 (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count);
153 176
154 if (list) { 177 mp_subcheck sc_other_updates = mp_subcheck_init();
155 qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp);
156 qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp);
157 178
158 for (int i = 0; i < sec_count; i++) { 179 xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available);
159 printf("%s (security)\n", secpackages_list[i]); 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);
185 }
186
187 if (config.list) {
188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
190 cmpstringp);
191
192 for (size_t i = 0; i < number_of_security_updates; i++) {
193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
194 secpackages_list[i]);
160 } 195 }
161 196
162 if (only_critical == false) { 197 if (!config.only_critical) {
163 for (int i = 0; i < packages_available - sec_count; i++) { 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
164 printf("%s\n", packages_list[i]); 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
200 packages_list[i]);
165 } 201 }
166 } 202 }
167 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
168 206
169 return result; 207 mp_exit(overall);
170} 208}
171 209
172/* process command-line arguments */ 210/* process command-line arguments */
173int process_arguments(int argc, char **argv) { 211check_apt_config_wrapper process_arguments(int argc, char **argv) {
212 enum {
213 /* Character for hidden input file option (for testing). */
214 INPUT_FILE_OPT = CHAR_MAX + 1,
215 output_format_index,
216 };
174 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
175 {"help", no_argument, 0, 'h'}, 218 {"help", no_argument, 0, 'h'},
176 {"verbose", no_argument, 0, 'v'}, 219 {"verbose", no_argument, 0, 'v'},
@@ -179,15 +222,21 @@ int process_arguments(int argc, char **argv) {
179 {"upgrade", optional_argument, 0, 'U'}, 222 {"upgrade", optional_argument, 0, 'U'},
180 {"no-upgrade", no_argument, 0, 'n'}, 223 {"no-upgrade", no_argument, 0, 'n'},
181 {"dist-upgrade", optional_argument, 0, 'd'}, 224 {"dist-upgrade", optional_argument, 0, 'd'},
182 {"list", no_argument, false, 'l'}, 225 {"list", no_argument, 0, 'l'},
183 {"include", required_argument, 0, 'i'}, 226 {"include", required_argument, 0, 'i'},
184 {"exclude", required_argument, 0, 'e'}, 227 {"exclude", required_argument, 0, 'e'},
185 {"critical", required_argument, 0, 'c'}, 228 {"critical", required_argument, 0, 'c'},
186 {"only-critical", no_argument, 0, 'o'}, 229 {"only-critical", no_argument, 0, 'o'},
187 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
188 {"packages-warning", required_argument, 0, 'w'}, 231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
189 {0, 0, 0, 0}}; 233 {0, 0, 0, 0}};
190 234
235 check_apt_config_wrapper result = {
236 .errorcode = OK,
237 .config = check_apt_config_init(),
238 };
239
191 while (true) { 240 while (true) {
192 int option_char = 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);
193 242
@@ -209,96 +258,110 @@ int process_arguments(int argc, char **argv) {
209 timeout_interval = atoi(optarg); 258 timeout_interval = atoi(optarg);
210 break; 259 break;
211 case 'd': 260 case 'd':
212 upgrade = DIST_UPGRADE; 261 result.config.upgrade = DIST_UPGRADE;
213 if (optarg != NULL) { 262 if (optarg != NULL) {
214 upgrade_opts = strdup(optarg); 263 result.config.upgrade_opts = strdup(optarg);
215 if (upgrade_opts == NULL) { 264 if (result.config.upgrade_opts == NULL) {
216 die(STATE_UNKNOWN, "strdup failed"); 265 die(STATE_UNKNOWN, "strdup failed");
217 } 266 }
218 } 267 }
219 break; 268 break;
220 case 'U': 269 case 'U':
221 upgrade = UPGRADE; 270 result.config.upgrade = UPGRADE;
222 if (optarg != NULL) { 271 if (optarg != NULL) {
223 upgrade_opts = strdup(optarg); 272 result.config.upgrade_opts = strdup(optarg);
224 if (upgrade_opts == NULL) { 273 if (result.config.upgrade_opts == NULL) {
225 die(STATE_UNKNOWN, "strdup failed"); 274 die(STATE_UNKNOWN, "strdup failed");
226 } 275 }
227 } 276 }
228 break; 277 break;
229 case 'n': 278 case 'n':
230 upgrade = NO_UPGRADE; 279 result.config.upgrade = NO_UPGRADE;
231 break; 280 break;
232 case 'u': 281 case 'u':
233 do_update = true; 282 result.config.do_update = true;
234 if (optarg != NULL) { 283 if (optarg != NULL) {
235 update_opts = strdup(optarg); 284 result.config.update_opts = strdup(optarg);
236 if (update_opts == NULL) { 285 if (result.config.update_opts == NULL) {
237 die(STATE_UNKNOWN, "strdup failed"); 286 die(STATE_UNKNOWN, "strdup failed");
238 } 287 }
239 } 288 }
240 break; 289 break;
241 case 'l': 290 case 'l':
242 list = true; 291 result.config.list = true;
243 break; 292 break;
244 case 'i': 293 case 'i':
245 do_include = add_to_regexp(do_include, optarg); 294 result.config.do_include = add_to_regexp(result.config.do_include, optarg);
246 break; 295 break;
247 case 'e': 296 case 'e':
248 do_exclude = add_to_regexp(do_exclude, optarg); 297 result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg);
249 break; 298 break;
250 case 'c': 299 case 'c':
251 do_critical = add_to_regexp(do_critical, optarg); 300 result.config.do_critical = add_to_regexp(result.config.do_critical, optarg);
252 break; 301 break;
253 case 'o': 302 case 'o':
254 only_critical = true; 303 result.config.only_critical = true;
255 break; 304 break;
256 case INPUT_FILE_OPT: 305 case INPUT_FILE_OPT:
257 input_filename = optarg; 306 result.config.input_filename = optarg;
258 break; 307 break;
259 case 'w': 308 case 'w':
260 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;
261 break; 321 break;
322 }
262 default: 323 default:
263 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
264 usage5(); 325 usage5();
265 } 326 }
266 } 327 }
267 328
268 return OK; 329 return result;
269} 330}
270 331
271/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
272run_upgrade_result run_upgrade(void) { 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
273 regex_t ereg; 334 const char *do_exclude, const char *do_critical,
335 const char *upgrade_opts, const char *input_filename) {
336 regex_t exclude_regex;
274 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
275 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
276 339
277 run_upgrade_result result = { 340 run_upgrade_result result = {
278 .errorcode = STATE_UNKNOWN, 341 .errorcode = OK,
279 }; 342 };
280 343
281 if (upgrade == NO_UPGRADE) { 344 if (upgrade == NO_UPGRADE) {
282 result.errorcode = STATE_OK; 345 result.errorcode = OK;
283 return result; 346 return result;
284 } 347 }
285 348
286 int regres = 0; 349 int regres = 0;
287 regex_t ireg; 350 regex_t include_regex;
288 char rerrbuf[64]; 351 char rerrbuf[64];
289 /* compile the regexps */ 352 /* compile the regexps */
290 if (do_include != NULL) { 353 if (do_include != NULL) {
291 regres = regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
292 if (regres != 0) { 355 if (regres != 0) {
293 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
294 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
295 } 358 }
296 } 359 }
297 360
298 if (do_exclude != NULL) { 361 if (do_exclude != NULL) {
299 regres = regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
300 if (regres != 0) { 363 if (regres != 0) {
301 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
302 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
303 } 366 }
304 } 367 }
@@ -307,12 +370,12 @@ run_upgrade_result run_upgrade(void) {
307 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
308 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
309 if (regres != 0) { 372 if (regres != 0) {
310 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
311 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
312 } 375 }
313 376
314 struct output chld_out; 377 output chld_out;
315 struct output chld_err; 378 output chld_err;
316 char *cmdline = NULL; 379 char *cmdline = NULL;
317 cmdline = construct_cmdline(upgrade, upgrade_opts); 380 cmdline = construct_cmdline(upgrade, upgrade_opts);
318 if (input_filename != NULL) { 381 if (input_filename != NULL) {
@@ -323,16 +386,15 @@ run_upgrade_result run_upgrade(void) {
323 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
324 } 387 }
325 388
326 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
327 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
328 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
329 if (result.errorcode != STATE_OK) { 392 result.exec_warning = true;
330 exec_warning = 1; 393 result.errorcode = ERROR;
331 result.errorcode = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
332 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
333 } 395 }
334 396
335 char **pkglist = malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
336 if (!pkglist) { 398 if (!pkglist) {
337 die(STATE_UNKNOWN, "malloc failed!\n"); 399 die(STATE_UNKNOWN, "malloc failed!\n");
338 } 400 }
@@ -350,27 +412,31 @@ run_upgrade_result run_upgrade(void) {
350 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
351 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
352 */ 414 */
353 int package_counter = 0; 415 size_t package_counter = 0;
354 int security_package_counter = 0; 416 size_t security_package_counter = 0;
355 for (size_t i = 0; i < chld_out.lines; i++) { 417 for (size_t i = 0; i < chld_out.lines; i++) {
356 if (verbose) { 418 if (verbose) {
357 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
358 } 420 }
421
359 /* if it is a package we care about */ 422 /* if it is a package we care about */
360 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 &&
361 (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)) {
362 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
363 * list of stuff to exclude */ 426 * list of stuff to exclude */
364 if (do_exclude == NULL || regexec(&ereg, chld_out.line[i], 0, NULL, 0) != 0) { 427 if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) {
365 package_counter++; 428 package_counter++;
366 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
367 security_package_counter++; 430 security_package_counter++;
431
368 if (verbose) { 432 if (verbose) {
369 printf("*"); 433 printf("*");
370 } 434 }
435
371 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); 436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
372 } else { 437 } else {
373 (pkglist)[package_counter - security_package_counter - 1] = pkg_name(chld_out.line[i]); 438 (pkglist)[package_counter - security_package_counter - 1] =
439 pkg_name(chld_out.line[i]);
374 } 440 }
375 if (verbose) { 441 if (verbose) {
376 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
@@ -378,6 +444,7 @@ run_upgrade_result run_upgrade(void) {
378 } 444 }
379 } 445 }
380 } 446 }
447
381 result.package_count = package_counter; 448 result.package_count = package_counter;
382 result.security_package_count = security_package_counter; 449 result.security_package_count = security_package_counter;
383 result.packages_list = pkglist; 450 result.packages_list = pkglist;
@@ -385,42 +452,56 @@ run_upgrade_result run_upgrade(void) {
385 452
386 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
387 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
388 stderr_warning = 1; 455 stderr_warning = true;
389 result.errorcode = max_state(result.errorcode, STATE_WARNING); 456 result.errorcode = ERROR;
457
390 if (verbose) { 458 if (verbose) {
391 for (size_t i = 0; i < chld_err.lines; i++) { 459 for (size_t i = 0; i < chld_err.lines; i++) {
392 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
393 } 461 }
394 } 462 }
395 } 463 }
464
396 if (do_include != NULL) { 465 if (do_include != NULL) {
397 regfree(&ireg); 466 regfree(&include_regex);
398 } 467 }
468
399 regfree(&sreg); 469 regfree(&sreg);
470
400 if (do_exclude != NULL) { 471 if (do_exclude != NULL) {
401 regfree(&ereg); 472 regfree(&exclude_regex);
402 } 473 }
474
403 free(cmdline); 475 free(cmdline);
476
404 return result; 477 return result;
405} 478}
406 479
407/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
408int run_update(void) { 481run_update_result run_update(char *update_opts) {
409 int result = STATE_UNKNOWN;
410 char *cmdline; 482 char *cmdline;
411 /* run the update */ 483 /* run the update */
412 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
413 485
414 struct output chld_out; 486 run_update_result result = {
415 struct output chld_err; 487 .exec_warning = false,
416 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 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);
417 /* 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.
418 * 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
419 * a critical error. */ 500 * a critical error. */
420 if (result != 0) { 501 if (cmd_error != 0) {
421 exec_warning = 1; 502 exec_warning = true;
422 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
423 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
424 } 505 }
425 506
426 if (verbose) { 507 if (verbose) {
@@ -431,22 +512,25 @@ int run_update(void) {
431 512
432 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
433 if (chld_err.buflen) { 514 if (chld_err.buflen) {
434 stderr_warning = 1; 515 stderr_warning = true;
435 result = max_state(result, STATE_WARNING); 516 result.sc = mp_set_subcheck_state(
517 result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING));
436 if (verbose) { 518 if (verbose) {
437 for (size_t i = 0; i < chld_err.lines; i++) { 519 for (size_t i = 0; i < chld_err.lines; i++) {
438 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
439 } 521 }
440 } 522 }
441 } 523 }
524
442 free(cmdline); 525 free(cmdline);
526
443 return result; 527 return result;
444} 528}
445 529
446char *pkg_name(char *line) { 530char *pkg_name(char *line) {
447 char *start = line + strlen(PKGINST_PREFIX); 531 char *start = line + strlen(PKGINST_PREFIX);
448 532
449 int len = strlen(start); 533 size_t len = strlen(start);
450 534
451 char *space = index(start, ' '); 535 char *space = index(start, ' ');
452 if (space != NULL) { 536 if (space != NULL) {
@@ -464,35 +548,37 @@ char *pkg_name(char *line) {
464 return pkg; 548 return pkg;
465} 549}
466 550
467int cmpstringp(const void *p1, const void *p2) { return strcmp(*(char *const *)p1, *(char *const *)p2); } 551int cmpstringp(const void *left_string, const void *right_string) {
552 return strcmp(*(char *const *)left_string, *(char *const *)right_string);
553}
468 554
469char *add_to_regexp(char *expr, const char *next) { 555char *add_to_regexp(char *expr, const char *next) {
470 char *re = NULL; 556 char *regex_string = NULL;
471 557
472 if (expr == NULL) { 558 if (expr == NULL) {
473 re = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1)); 559 regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1));
474 if (!re) { 560 if (!regex_string) {
475 die(STATE_UNKNOWN, "malloc failed!\n"); 561 die(STATE_UNKNOWN, "malloc failed!\n");
476 } 562 }
477 sprintf(re, "(%s)", next); 563 sprintf(regex_string, "(%s)", next);
478 } else { 564 } else {
479 /* resize it, adding an extra char for the new '|' separator */ 565 /* resize it, adding an extra char for the new '|' separator */
480 re = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1)); 566 regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1));
481 if (!re) { 567 if (!regex_string) {
482 die(STATE_UNKNOWN, "realloc failed!\n"); 568 die(STATE_UNKNOWN, "realloc failed!\n");
483 } 569 }
484 /* append it starting at ')' in the old re */ 570 /* append it starting at ')' in the old re */
485 sprintf((char *)(re + strlen(re) - 1), "|%s)", next); 571 sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next);
486 } 572 }
487 573
488 return re; 574 return regex_string;
489} 575}
490 576
491char *construct_cmdline(upgrade_type u, const char *opts) { 577char *construct_cmdline(upgrade_type upgrade, const char *opts) {
492 const char *opts_ptr = NULL; 578 const char *opts_ptr = NULL;
493 const char *aptcmd = NULL; 579 const char *aptcmd = NULL;
494 580
495 switch (u) { 581 switch (upgrade) {
496 case UPGRADE: 582 case UPGRADE:
497 if (opts == NULL) { 583 if (opts == NULL) {
498 opts_ptr = UPGRADE_DEFAULT_OPTS; 584 opts_ptr = UPGRADE_DEFAULT_OPTS;
@@ -519,7 +605,7 @@ char *construct_cmdline(upgrade_type u, const char *opts) {
519 break; 605 break;
520 } 606 }
521 607
522 int len = 0; 608 size_t len = 0;
523 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
524 len += strlen(opts_ptr) + 1; /* "opts " */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
525 len += strlen(aptcmd) + 1; /* "upgrade\0" */ 611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
@@ -557,7 +643,8 @@ void print_help(void) {
557 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); 643 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by"));
558 printf(" %s\n", _("name with security packages listed first.")); 644 printf(" %s\n", _("name with security packages listed first."));
559 printf(" %s\n", "-i, --include=REGEXP"); 645 printf(" %s\n", "-i, --include=REGEXP");
560 printf(" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); 646 printf(" %s\n",
647 _("Include only packages matching REGEXP. Can be specified multiple times"));
561 printf(" %s\n", _("the values will be combined together. Any packages matching this list")); 648 printf(" %s\n", _("the values will be combined together. Any packages matching this list"));
562 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); 649 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored."));
563 printf(" %s\n", _("Default is to include all packages.")); 650 printf(" %s\n", _("Default is to include all packages."));
@@ -566,7 +653,8 @@ void print_help(void) {
566 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values")); 653 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values"));
567 printf(" %s\n", _("will be combined together. Default is to exclude no packages.")); 654 printf(" %s\n", _("will be combined together. Default is to exclude no packages."));
568 printf(" %s\n", "-c, --critical=REGEXP"); 655 printf(" %s\n", "-c, --critical=REGEXP");
569 printf(" %s\n", _("If the full package information of any of the upgradable packages match")); 656 printf(" %s\n",
657 _("If the full package information of any of the upgradable packages match"));
570 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"));
571 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"));
572 printf(" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
@@ -575,15 +663,21 @@ void print_help(void) {
575 printf(" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
576 printf(" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
577 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"));
578 printf(" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); 666 printf(" %s\n",
667 _("of upgrades will be printed, but any non-critical upgrades will not cause"));
579 printf(" %s\n", _("the plugin to return WARNING status.")); 668 printf(" %s\n", _("the plugin to return WARNING status."));
580 printf(" %s\n", "-w, --packages-warning"); 669 printf(" %s\n", "-w, --packages-warning");
581 printf(" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); 670 printf(" %s\n",
671 _("Minimum number of packages available for upgrade to return WARNING status."));
582 printf(" %s\n\n", _("Default is 1 package.")); 672 printf(" %s\n\n", _("Default is 1 package."));
583 673
584 printf("%s\n\n", _("The following options require root privileges and should be used with care:")); 674 printf(UT_OUTPUT_FORMAT);
675
676 printf("%s\n\n",
677 _("The following options require root privileges and should be used with care:"));
585 printf(" %s\n", "-u, --update=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
586 printf(" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); 679 printf(" %s\n",
680 _("First perform an 'apt-get update'. An optional OPTS parameter overrides"));
587 printf(" %s\n", _("the default options. Note: you may also need to adjust the global")); 681 printf(" %s\n", _("the default options. Note: you may also need to adjust the global"));
588 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); 682 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get"));
589 printf(" %s\n", _("upgrade is expected to take longer than the default timeout.")); 683 printf(" %s\n", _("upgrade is expected to take longer than the default timeout."));
@@ -592,8 +686,10 @@ void print_help(void) {
592 printf(" %s\n", _("apt-get will be run with these command line options instead of the")); 686 printf(" %s\n", _("apt-get will be run with these command line options instead of the"));
593 printf(" %s", _("default ")); 687 printf(" %s", _("default "));
594 printf("(%s).\n", UPGRADE_DEFAULT_OPTS); 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
595 printf(" %s\n", _("Note that you may be required to have root privileges if you do not use")); 689 printf(" %s\n",
596 printf(" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); 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"));
597 printf(" %s\n", "-d, --dist-upgrade=OPTS"); 693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
598 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); 694 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS"));
599 printf(" %s\n", _("can be provided to override the default options.")); 695 printf(" %s\n", _("can be provided to override the default options."));
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 905b2393..a43c0d34 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -32,48 +32,29 @@ const char *email = "devel@monitoring-plugins.org";
32 32
33#include "common.h" 33#include "common.h"
34#include "utils.h" 34#include "utils.h"
35#include "netutils.h"
36#include "utils_cmd.h" 35#include "utils_cmd.h"
36#include "check_by_ssh.d/config.h"
37#include "states.h"
37 38
38#ifndef NP_MAXARGS 39#ifndef NP_MAXARGS
39# define NP_MAXARGS 1024 40# define NP_MAXARGS 1024
40#endif 41#endif
41 42
42static int process_arguments(int /*argc*/, char ** /*argv*/); 43typedef struct {
43static int validate_arguments(void); 44 int errorcode;
44static void comm_append(const char * /*str*/); 45 check_by_ssh_config config;
46} check_by_ssh_config_wrapper;
47static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
48static check_by_ssh_config_wrapper
49 validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/);
50
51static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/);
45static void print_help(void); 52static void print_help(void);
46void print_usage(void); 53void print_usage(void);
47 54
48static unsigned int commands = 0;
49static unsigned int services = 0;
50static int skip_stdout = 0;
51static int skip_stderr = 0;
52static int warn_on_stderr = 0;
53static bool unknown_timeout = false;
54static char *remotecmd = NULL;
55static char **commargv = NULL;
56static int commargc = 0;
57static char *hostname = NULL;
58static char *outputfile = NULL;
59static char *host_shortname = NULL;
60static char **service;
61static bool passive = false;
62static bool verbose = false; 55static bool verbose = false;
63 56
64int main(int argc, char **argv) { 57int main(int argc, char **argv) {
65
66 char *status_text;
67 int cresult;
68 int result = STATE_UNKNOWN;
69 time_t local_time;
70 FILE *file_pointer = NULL;
71 output chld_out;
72 output chld_err;
73
74 remotecmd = "";
75 comm_append(SSH_COMMAND);
76
77 setlocale(LC_ALL, ""); 58 setlocale(LC_ALL, "");
78 bindtextdomain(PACKAGE, LOCALEDIR); 59 bindtextdomain(PACKAGE, LOCALEDIR);
79 textdomain(PACKAGE); 60 textdomain(PACKAGE);
@@ -81,9 +62,14 @@ int main(int argc, char **argv) {
81 /* Parse extra opts if any */ 62 /* Parse extra opts if any */
82 argv = np_extra_opts(&argc, argv, progname); 63 argv = np_extra_opts(&argc, argv, progname);
83 64
65 check_by_ssh_config_wrapper tmp_config = process_arguments(argc, argv);
66
84 /* process arguments */ 67 /* process arguments */
85 if (process_arguments(argc, argv) == ERROR) 68 if (tmp_config.errorcode == ERROR) {
86 usage_va(_("Could not parse arguments")); 69 usage_va(_("Could not parse arguments"));
70 }
71
72 const check_by_ssh_config config = tmp_config.config;
87 73
88 /* Set signal handling and alarm timeout */ 74 /* Set signal handling and alarm timeout */
89 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 75 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
@@ -93,47 +79,67 @@ int main(int argc, char **argv) {
93 79
94 /* run the command */ 80 /* run the command */
95 if (verbose) { 81 if (verbose) {
96 printf("Command: %s\n", commargv[0]); 82 printf("Command: %s\n", config.cmd.commargv[0]);
97 for (int i = 1; i < commargc; i++) 83 for (int i = 1; i < config.cmd.commargc; i++) {
98 printf("Argument %i: %s\n", i, commargv[i]); 84 printf("Argument %i: %s\n", i, config.cmd.commargv[i]);
85 }
99 } 86 }
100 87
101 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);
102 91
103 /* 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 */
104 if (result == 255 && unknown_timeout) { 93 if (result == 255 && config.unknown_timeout) {
105 printf(_("SSH connection failed: %s\n"), chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); 94 printf(_("SSH connection failed: %s\n"),
95 chld_err.lines > 0 ? chld_err.line[0] : "(no error output)");
106 return STATE_UNKNOWN; 96 return STATE_UNKNOWN;
107 } 97 }
108 98
109 if (verbose) { 99 if (verbose) {
110 for (size_t i = 0; i < chld_out.lines; i++) 100 for (size_t i = 0; i < chld_out.lines; i++) {
111 printf("stdout: %s\n", chld_out.line[i]); 101 printf("stdout: %s\n", chld_out.line[i]);
112 for (size_t i = 0; i < chld_err.lines; i++) 102 }
103 for (size_t i = 0; i < chld_err.lines; i++) {
113 printf("stderr: %s\n", chld_err.line[i]); 104 printf("stderr: %s\n", chld_err.line[i]);
105 }
114 } 106 }
115 107
116 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 */
117 skip_stdout = chld_out.lines; 110 skip_stdout = chld_out.lines;
118 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 */
119 skip_stderr = chld_err.lines; 117 skip_stderr = chld_err.lines;
118 } else {
119 skip_stderr = config.skip_stderr;
120 }
120 121
121 /* UNKNOWN or worse if (non-skipped) output found on stderr */ 122 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */
122 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)) {
123 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); 124 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]);
124 if (warn_on_stderr) 125 if (config.unknown_on_stderr) {
126 return max_state_alt(result, STATE_UNKNOWN);
127 } else if (config.warn_on_stderr) {
125 return max_state_alt(result, STATE_WARNING); 128 return max_state_alt(result, STATE_WARNING);
126 return max_state_alt(result, STATE_UNKNOWN); 129 }
127 } 130 }
128 131
129 /* this is simple if we're not supposed to be passive. 132 /* this is simple if we're not supposed to be passive.
130 * Wrap up quickly and keep the tricks below */ 133 * Wrap up quickly and keep the tricks below */
131 if (!passive) { 134 if (!config.passive) {
132 if (chld_out.lines > (size_t)skip_stdout) 135 if (chld_out.lines > (size_t)skip_stdout) {
133 for (size_t i = skip_stdout; i < chld_out.lines; i++) 136 for (size_t i = skip_stdout; i < chld_out.lines; i++) {
134 puts(chld_out.line[i]); 137 puts(chld_out.line[i]);
135 else 138 }
136 printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), state_text(result), remotecmd, result); 139 } else {
140 printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"),
141 state_text(result), config.remotecmd, result);
142 }
137 return result; /* return error status from remote command */ 143 return result; /* return error status from remote command */
138 } 144 }
139 145
@@ -142,78 +148,95 @@ int main(int argc, char **argv) {
142 */ 148 */
143 149
144 /* process output */ 150 /* process output */
145 if (!(file_pointer = fopen(outputfile, "a"))) { 151 FILE *file_pointer = NULL;
146 printf(_("SSH WARNING: could not open %s\n"), outputfile); 152 if (!(file_pointer = fopen(config.outputfile, "a"))) {
153 printf(_("SSH WARNING: could not open %s\n"), config.outputfile);
147 exit(STATE_UNKNOWN); 154 exit(STATE_UNKNOWN);
148 } 155 }
149 156
150 local_time = time(NULL); 157 time_t local_time = time(NULL);
151 commands = 0; 158 unsigned int commands = 0;
159 char *status_text;
160 int cresult;
152 for (size_t i = skip_stdout; i < chld_out.lines; i++) { 161 for (size_t i = skip_stdout; i < chld_out.lines; i++) {
153 status_text = chld_out.line[i++]; 162 status_text = chld_out.line[i++];
154 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) {
155 die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); 164 die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname);
165 }
156 166
157 if (service[commands] && status_text && sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { 167 if (config.service[commands] && status_text &&
158 fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, host_shortname, service[commands++], 168 sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) {
159 cresult, status_text); 169 fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
170 (int)local_time, config.host_shortname, config.service[commands++], cresult,
171 status_text);
160 } 172 }
161 } 173 }
162 174
163 /* Multiple commands and passive checking should always return OK */ 175 /* Multiple commands and passive checking should always return OK */
164 return result; 176 exit(result);
165} 177}
166 178
167/* process command-line arguments */ 179/* process command-line arguments */
168int process_arguments(int argc, char **argv) { 180check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
169 int c; 181 static struct option longopts[] = {
170 char *p1; 182 {"version", no_argument, 0, 'V'},
171 char *p2; 183 {"help", no_argument, 0, 'h'},
184 {"verbose", no_argument, 0, 'v'},
185 {"fork", no_argument, 0, 'f'},
186 {"timeout", required_argument, 0, 't'},
187 {"unknown-timeout", no_argument, 0, 'U'},
188 {"host", required_argument, 0, 'H'}, /* backward compatibility */
189 {"hostname", required_argument, 0, 'H'},
190 {"port", required_argument, 0, 'p'},
191 {"output", required_argument, 0, 'O'},
192 {"name", required_argument, 0, 'n'},
193 {"services", required_argument, 0, 's'},
194 {"identity", required_argument, 0, 'i'},
195 {"user", required_argument, 0, 'u'}, /* backwards compatibility */
196 {"logname", required_argument, 0, 'l'},
197 {"command", required_argument, 0, 'C'},
198 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
199 {"skip-stdout", optional_argument, 0, 'S'},
200 {"skip-stderr", optional_argument, 0, 'E'},
201 {"unknown-on-stderr", no_argument, 0, 'e'},
202 {"warn-on-stderr", no_argument, 0, 'W'},
203 {"proto1", no_argument, 0, '1'},
204 {"proto2", no_argument, 0, '2'},
205 {"use-ipv4", no_argument, 0, '4'},
206 {"use-ipv6", no_argument, 0, '6'},
207 {"ssh-option", required_argument, 0, 'o'},
208 {"quiet", no_argument, 0, 'q'},
209 {"configfile", optional_argument, 0, 'F'},
210 {0, 0, 0, 0}};
211
212 check_by_ssh_config_wrapper result = {
213 .errorcode = OK,
214 .config = check_by_ssh_config_init(),
215 };
216
217 if (argc < 2) {
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 }
227
228 result.config.cmd = comm_append(result.config.cmd, SSH_COMMAND);
172 229
173 int option = 0; 230 int option = 0;
174 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 231 while (true) {
175 {"help", no_argument, 0, 'h'}, 232 int opt_index =
176 {"verbose", no_argument, 0, 'v'}, 233 getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option);
177 {"fork", no_argument, 0, 'f'}, 234
178 {"timeout", required_argument, 0, 't'}, 235 if (opt_index == -1 || opt_index == EOF) {
179 {"unknown-timeout", no_argument, 0, 'U'},
180 {"host", required_argument, 0, 'H'}, /* backward compatibility */
181 {"hostname", required_argument, 0, 'H'},
182 {"port", required_argument, 0, 'p'},
183 {"output", required_argument, 0, 'O'},
184 {"name", required_argument, 0, 'n'},
185 {"services", required_argument, 0, 's'},
186 {"identity", required_argument, 0, 'i'},
187 {"user", required_argument, 0, 'u'},
188 {"logname", required_argument, 0, 'l'},
189 {"command", required_argument, 0, 'C'},
190 {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
191 {"skip-stdout", optional_argument, 0, 'S'},
192 {"skip-stderr", optional_argument, 0, 'E'},
193 {"warn-on-stderr", no_argument, 0, 'W'},
194 {"proto1", no_argument, 0, '1'},
195 {"proto2", no_argument, 0, '2'},
196 {"use-ipv4", no_argument, 0, '4'},
197 {"use-ipv6", no_argument, 0, '6'},
198 {"ssh-option", required_argument, 0, 'o'},
199 {"quiet", no_argument, 0, 'q'},
200 {"configfile", optional_argument, 0, 'F'},
201 {0, 0, 0, 0}};
202
203 if (argc < 2)
204 return ERROR;
205
206 for (c = 1; c < argc; c++)
207 if (strcmp("-to", argv[c]) == 0)
208 strcpy(argv[c], "-t");
209
210 while (1) {
211 c = getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option);
212
213 if (c == -1 || c == EOF)
214 break; 236 break;
237 }
215 238
216 switch (c) { 239 switch (opt_index) {
217 case 'V': /* version */ 240 case 'V': /* version */
218 print_revision(progname, NP_VERSION); 241 print_revision(progname, NP_VERSION);
219 exit(STATE_UNKNOWN); 242 exit(STATE_UNKNOWN);
@@ -224,162 +247,202 @@ int process_arguments(int argc, char **argv) {
224 verbose = true; 247 verbose = true;
225 break; 248 break;
226 case 't': /* timeout period */ 249 case 't': /* timeout period */
227 if (!is_integer(optarg)) 250 if (!is_integer(optarg)) {
228 usage_va(_("Timeout interval must be a positive integer")); 251 usage_va(_("Timeout interval must be a positive integer"));
229 else 252 } else {
230 timeout_interval = atoi(optarg); 253 timeout_interval = atoi(optarg);
254 }
231 break; 255 break;
232 case 'U': 256 case 'U':
233 unknown_timeout = true; 257 result.config.unknown_timeout = true;
234 break; 258 break;
235 case 'H': /* host */ 259 case 'H': /* host */
236 hostname = optarg; 260 result.config.hostname = optarg;
237 break; 261 break;
238 case 'p': /* port number */ 262 case 'p': /* port number */
239 if (!is_integer(optarg)) 263 if (!is_integer(optarg)) {
240 usage_va(_("Port must be a positive integer")); 264 usage_va(_("Port must be a positive integer"));
241 comm_append("-p"); 265 }
242 comm_append(optarg); 266 result.config.cmd = comm_append(result.config.cmd, "-p");
267 result.config.cmd = comm_append(result.config.cmd, optarg);
243 break; 268 break;
244 case 'O': /* output file */ 269 case 'O': /* output file */
245 outputfile = optarg; 270 result.config.outputfile = optarg;
246 passive = true; 271 result.config.passive = true;
247 break; 272 break;
248 case 's': /* description of service to check */ 273 case 's': /* description of service to check */ {
274 char *p1;
275 char *p2;
276
249 p1 = optarg; 277 p1 = optarg;
250 service = realloc(service, (++services) * sizeof(char *)); 278 result.config.service = realloc(result.config.service,
279 (++result.config.number_of_services) * sizeof(char *));
251 while ((p2 = index(p1, ':'))) { 280 while ((p2 = index(p1, ':'))) {
252 *p2 = '\0'; 281 *p2 = '\0';
253 service[services - 1] = p1; 282 result.config.service[result.config.number_of_services - 1] = p1;
254 service = realloc(service, (++services) * sizeof(char *)); 283 result.config.service = realloc(
284 result.config.service, (++result.config.number_of_services) * sizeof(char *));
255 p1 = p2 + 1; 285 p1 = p2 + 1;
256 } 286 }
257 service[services - 1] = p1; 287 result.config.service[result.config.number_of_services - 1] = p1;
258 break; 288 break;
259 case 'n': /* short name of host in the monitoring configuration */ 289 case 'n': /* short name of host in the monitoring configuration */
260 host_shortname = optarg; 290 result.config.host_shortname = optarg;
261 break; 291 } break;
262
263 case 'u': 292 case 'u':
264 comm_append("-l"); 293 result.config.cmd = comm_append(result.config.cmd, "-l");
265 comm_append(optarg); 294 result.config.cmd = comm_append(result.config.cmd, optarg);
266 break; 295 break;
267 case 'l': /* login name */ 296 case 'l': /* login name */
268 comm_append("-l"); 297 result.config.cmd = comm_append(result.config.cmd, "-l");
269 comm_append(optarg); 298 result.config.cmd = comm_append(result.config.cmd, optarg);
270 break; 299 break;
271 case 'i': /* identity */ 300 case 'i': /* identity */
272 comm_append("-i"); 301 result.config.cmd = comm_append(result.config.cmd, "-i");
273 comm_append(optarg); 302 result.config.cmd = comm_append(result.config.cmd, optarg);
274 break; 303 break;
275 304
276 case '1': /* Pass these switches directly to ssh */ 305 case '1': /* Pass these switches directly to ssh */
277 comm_append("-1"); 306 result.config.cmd = comm_append(result.config.cmd, "-1");
278 break; 307 break;
279 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 */
280 comm_append("-2"); 309 result.config.cmd = comm_append(result.config.cmd, "-2");
281 break; 310 break;
282 case '4': /* -4 for IPv4 */ 311 case '4': /* -4 for IPv4 */
283 comm_append("-4"); 312 result.config.cmd = comm_append(result.config.cmd, "-4");
284 break; 313 break;
285 case '6': /* -6 for IPv6 */ 314 case '6': /* -6 for IPv6 */
286 comm_append("-6"); 315 result.config.cmd = comm_append(result.config.cmd, "-6");
287 break; 316 break;
288 case 'f': /* fork to background */ 317 case 'f': /* fork to background */
289 comm_append("-f"); 318 result.config.cmd = comm_append(result.config.cmd, "-f");
290 break; 319 break;
291 case 'C': /* Command for remote machine */ 320 case 'C': /* Command for remote machine */
292 commands++; 321 result.config.commands++;
293 if (commands > 1) 322 if (result.config.commands > 1) {
294 xasprintf(&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 323 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;",
295 xasprintf(&remotecmd, "%s%s", remotecmd, optarg); 324 result.config.remotecmd);
325 }
326 xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg);
296 break; 327 break;
297 case 'S': /* skip n (or all) lines on stdout */ 328 case 'S': /* skip n (or all) lines on stdout */
298 if (optarg == NULL) 329 if (optarg == NULL) {
299 skip_stdout = -1; /* skip all output on stdout */ 330 result.config.skip_stdout = -1; /* skip all output on stdout */
300 else if (!is_integer(optarg)) 331 } else if (!is_integer(optarg)) {
301 usage_va(_("skip-stdout argument must be an integer")); 332 usage_va(_("skip-stdout argument must be an integer"));
302 else 333 } else {
303 skip_stdout = atoi(optarg); 334 result.config.skip_stdout = atoi(optarg);
335 }
304 break; 336 break;
305 case 'E': /* skip n (or all) lines on stderr */ 337 case 'E': /* skip n (or all) lines on stderr */
306 if (optarg == NULL) 338 if (optarg == NULL) {
307 skip_stderr = -1; /* skip all output on stderr */ 339 result.config.skip_stderr = -1; /* skip all output on stderr */
308 else if (!is_integer(optarg)) 340 } else if (!is_integer(optarg)) {
309 usage_va(_("skip-stderr argument must be an integer")); 341 usage_va(_("skip-stderr argument must be an integer"));
310 else 342 } else {
311 skip_stderr = atoi(optarg); 343 result.config.skip_stderr = atoi(optarg);
344 }
345 break;
346 case 'e': /* exit with unknown if there is an output on stderr */
347 result.config.unknown_on_stderr = true;
312 break; 348 break;
313 case 'W': /* exit with warning if there is an output on stderr */ 349 case 'W': /* exit with warning if there is an output on stderr */
314 warn_on_stderr = 1; 350 result.config.warn_on_stderr = true;
315 break; 351 break;
316 case 'o': /* Extra options for the ssh command */ 352 case 'o': /* Extra options for the ssh command */
317 comm_append("-o"); 353 result.config.cmd = comm_append(result.config.cmd, "-o");
318 comm_append(optarg); 354 result.config.cmd = comm_append(result.config.cmd, optarg);
319 break; 355 break;
320 case 'q': /* Tell the ssh command to be quiet */ 356 case 'q': /* Tell the ssh command to be quiet */
321 comm_append("-q"); 357 result.config.cmd = comm_append(result.config.cmd, "-q");
322 break; 358 break;
323 case 'F': /* ssh configfile */ 359 case 'F': /* ssh configfile */
324 comm_append("-F"); 360 result.config.cmd = comm_append(result.config.cmd, "-F");
325 comm_append(optarg); 361 result.config.cmd = comm_append(result.config.cmd, optarg);
326 break; 362 break;
327 default: /* help */ 363 default: /* help */
328 usage5(); 364 usage5();
329 } 365 }
330 } 366 }
331 367
332 c = optind; 368 int c = optind;
333 if (hostname == NULL) { 369 if (result.config.hostname == NULL) {
334 if (c <= argc) { 370 if (c <= argc) {
335 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);
336 } 372 }
337 hostname = argv[c++]; 373 result.config.hostname = argv[c++];
338 } 374 }
339 375
340 if (strlen(remotecmd) == 0) { 376 if (strlen(result.config.remotecmd) == 0) {
341 for (; c < argc; c++) 377 for (; c < argc; c++) {
342 if (strlen(remotecmd) > 0) 378 if (strlen(result.config.remotecmd) > 0) {
343 xasprintf(&remotecmd, "%s %s", remotecmd, argv[c]); 379 xasprintf(&result.config.remotecmd, "%s %s", result.config.remotecmd, argv[c]);
344 else 380 } else {
345 xasprintf(&remotecmd, "%s", argv[c]); 381 xasprintf(&result.config.remotecmd, "%s", argv[c]);
382 }
383 }
346 } 384 }
347 385
348 if (commands > 1 || passive) 386 if (result.config.commands > 1 || result.config.passive) {
349 xasprintf(&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd); 387 xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd);
388 }
350 389
351 if (remotecmd == NULL || strlen(remotecmd) <= 1) 390 if (result.config.remotecmd == NULL || strlen(result.config.remotecmd) <= 1) {
352 usage_va(_("No remotecmd")); 391 usage_va(_("No remotecmd"));
392 }
353 393
354 comm_append(hostname); 394 result.config.cmd = comm_append(result.config.cmd, result.config.hostname);
355 comm_append(remotecmd); 395 result.config.cmd = comm_append(result.config.cmd, result.config.remotecmd);
356 396
357 return validate_arguments(); 397 return validate_arguments(result);
358} 398}
359 399
360void comm_append(const char *str) { 400command_construct comm_append(command_construct cmd, const char *str) {
401
402 if (verbose) {
403 for (int i = 0; i < cmd.commargc; i++) {
404 printf("Current command: [%i] %s\n", i, cmd.commargv[i]);
405 }
406
407 printf("Appending: %s\n", str);
408 }
361 409
362 if (++commargc > NP_MAXARGS) 410 if (++cmd.commargc > NP_MAXARGS) {
363 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 }
364 413
365 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) {
366 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); 416 die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n"));
417 }
418
419 cmd.commargv[cmd.commargc - 1] = strdup(str);
420 cmd.commargv[cmd.commargc] = NULL;
367 421
368 commargv[commargc - 1] = strdup(str); 422 return cmd;
369 commargv[commargc] = NULL;
370} 423}
371 424
372int validate_arguments(void) { 425check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper config_wrapper) {
373 if (remotecmd == NULL || hostname == NULL) 426 if (config_wrapper.config.remotecmd == NULL || config_wrapper.config.hostname == NULL) {
374 return ERROR; 427 config_wrapper.errorcode = ERROR;
428 return config_wrapper;
429 }
375 430
376 if (passive && commands != services) 431 if (config_wrapper.config.passive &&
377 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 }
378 437
379 if (passive && host_shortname == NULL) 438 if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) {
380 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 }
381 444
382 return OK; 445 return config_wrapper;
383} 446}
384 447
385void print_help(void) { 448void print_help(void) {
@@ -410,10 +473,13 @@ void print_help(void) {
410 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); 473 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]"));
411 printf(" %s\n", "-E, --skip-stderr[=n]"); 474 printf(" %s\n", "-E, --skip-stderr[=n]");
412 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); 475 printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]"));
413 printf(" %s\n", "-W, --warn-on-stderr]"); 476 printf(" %s\n", "-e, --unknown-on-stderr");
414 printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); 477 printf(" %s\n", _("Exit with UNKNOWN, if there is output on STDERR"));
478 printf(" %s\n", "-W, --warn-on-stderr");
479 printf(" %s\n", _("Exit with WARNING, if there is output on STDERR"));
415 printf(" %s\n", "-f"); 480 printf(" %s\n", "-f");
416 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); 481 printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always "
482 "return OK if ssh is executed"));
417 printf(" %s\n", "-C, --command='COMMAND STRING'"); 483 printf(" %s\n", "-C, --command='COMMAND STRING'");
418 printf(" %s\n", _("command to execute on the remote machine")); 484 printf(" %s\n", _("command to execute on the remote machine"));
419 printf(" %s\n", "-l, --logname=USERNAME"); 485 printf(" %s\n", "-l, --logname=USERNAME");
@@ -432,7 +498,6 @@ void print_help(void) {
432 printf(" %s\n", _("Tell ssh to use this configfile [optional]")); 498 printf(" %s\n", _("Tell ssh to use this configfile [optional]"));
433 printf(" %s\n", "-q, --quiet"); 499 printf(" %s\n", "-q, --quiet");
434 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]"));
435 printf(UT_WARN_CRIT);
436 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 501 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
437 printf(" %s\n", "-U, --unknown-timeout"); 502 printf(" %s\n", "-U, --unknown-timeout");
438 printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); 503 printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL"));
@@ -450,7 +515,9 @@ void print_help(void) {
450 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); 515 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
451 printf("\n"); 516 printf("\n");
452 printf("%s\n", _("Examples:")); 517 printf("%s\n", _("Examples:"));
453 printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); 518 printf(
519 " %s\n",
520 "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
454 printf(" %s\n", "$ cat /tmp/foo"); 521 printf(" %s\n", "$ cat /tmp/foo");
455 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); 522 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
456 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); 523 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
@@ -462,7 +529,7 @@ void print_help(void) {
462void print_usage(void) { 529void print_usage(void) {
463 printf("%s\n", _("Usage:")); 530 printf("%s\n", _("Usage:"));
464 printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n" 531 printf(" %s -H <host> -C <command> [-fqvU] [-1|-2] [-4|-6]\n"
465 " [-S [lines]] [-E [lines]] [-W] [-t timeout] [-i identity]\n" 532 " [-S [lines]] [-E [lines]] [-e|-W] [-t timeout] [-i identity]\n"
466 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n" 533 " [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
467 " [-p port] [-o ssh-option] [-F configfile]\n", 534 " [-p port] [-o ssh-option] [-F configfile]\n",
468 progname); 535 progname);
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 b40c38c7..1cbdcd60 100644
--- a/plugins/check_cluster.c
+++ b/plugins/check_cluster.c
@@ -26,45 +26,25 @@ const char *progname = "check_cluster";
26const char *copyright = "2000-2024"; 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"
32 34#include "check_cluster.d/config.h"
33enum {
34 CHECK_SERVICES = 1,
35 CHECK_HOSTS = 2
36};
37 35
38static void print_help(void); 36static void print_help(void);
39void print_usage(void); 37void print_usage(void);
40 38
41static int total_services_ok = 0;
42static int total_services_warning = 0;
43static int total_services_unknown = 0;
44static int total_services_critical = 0;
45
46static int total_hosts_up = 0;
47static int total_hosts_down = 0;
48static int total_hosts_unreachable = 0;
49
50static char *warn_threshold;
51static char *crit_threshold;
52
53static int check_type = CHECK_SERVICES;
54
55static char *data_vals = NULL;
56static char *label = NULL;
57
58static int verbose = 0; 39static int verbose = 0;
59 40
60static int process_arguments(int /*argc*/, char ** /*argv*/); 41typedef struct {
42 int errorcode;
43 check_cluster_config config;
44} check_cluster_config_wrapper;
45static check_cluster_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
61 46
62int main(int argc, char **argv) { 47int 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, ""); 48 setlocale(LC_ALL, "");
69 bindtextdomain(PACKAGE, LOCALEDIR); 49 bindtextdomain(PACKAGE, LOCALEDIR);
70 textdomain(PACKAGE); 50 textdomain(PACKAGE);
@@ -72,20 +52,35 @@ int main(int argc, char **argv) {
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
86 data_val = atoi(ptr); 81 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++;
@@ -119,101 +114,141 @@ int main(int argc, char **argv) {
119 } 114 }
120 } 115 }
121 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);
120
122 /* return the status of the cluster */ 121 /* return the status of the cluster */
123 if (check_type == CHECK_SERVICES) { 122 if (config.check_type == CHECK_SERVICES) {
124 return_code = get_status(total_services_warning + total_services_unknown + total_services_critical, thresholds); 123 sc_real_test = mp_set_subcheck_state(
125 printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", state_text(return_code), 124 sc_real_test,
126 (label == NULL) ? "Service cluster" : label, total_services_ok, total_services_warning, total_services_unknown, 125 get_status(total_services_warning + total_services_unknown + total_services_critical,
127 total_services_critical); 126 config.thresholds));
127 xasprintf(&sc_real_test.output, "%s: %d ok, %d warning, %d unknown, %d critical",
128 (config.label == NULL) ? "Service cluster" : config.label, total_services_ok,
129 total_services_warning, total_services_unknown, total_services_critical);
128 } else { 130 } else {
129 return_code = get_status(total_hosts_down + total_hosts_unreachable, thresholds); 131 sc_real_test = mp_set_subcheck_state(
130 printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", state_text(return_code), (label == NULL) ? "Host cluster" : label, 132 sc_real_test,
131 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);
132 } 137 }
133 138
134 return return_code; 139 mp_add_subcheck_to_check(&overall, sc_real_test);
140
141 mp_exit(overall);
135} 142}
136 143
137int process_arguments(int argc, char **argv) { 144check_cluster_config_wrapper process_arguments(int argc, char **argv) {
138 int c; 145 enum {
139 char *ptr; 146 output_format_index = CHAR_MAX + 1,
140 int option = 0; 147 };
141 static struct option longopts[] = {{"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'}, 148
142 {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'}, 149 static struct option longopts[] = {{"data", required_argument, 0, 'd'},
143 {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'}, 150 {"warning", required_argument, 0, 'w'},
144 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 151 {"critical", required_argument, 0, 'c'},
145 {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}}; 152 {"label", required_argument, 0, 'l'},
153 {"host", no_argument, 0, 'h'},
154 {"service", no_argument, 0, 's'},
155 {"verbose", no_argument, 0, 'v'},
156 {"version", no_argument, 0, 'V'},
157 {"help", no_argument, 0, 'H'},
158 {"output-format", required_argument, 0, output_format_index},
159 {0, 0, 0, 0}};
160
161 check_cluster_config_wrapper result = {
162 .errorcode = OK,
163 .config = check_cluster_config_init(),
164 };
146 165
147 /* no options were supplied */ 166 /* no options were supplied */
148 if (argc < 2) 167 if (argc < 2) {
149 return ERROR; 168 result.errorcode = ERROR;
150 169 return result;
151 while (1) { 170 }
152 171
153 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);
154 177
155 if (c == -1 || c == EOF || c == 1) 178 if (option_index == -1 || option_index == EOF || option_index == 1) {
156 break; 179 break;
180 }
157 181
158 switch (c) { 182 switch (option_index) {
159
160 case 'h': /* host cluster */ 183 case 'h': /* host cluster */
161 check_type = CHECK_HOSTS; 184 result.config.check_type = CHECK_HOSTS;
162 break; 185 break;
163
164 case 's': /* service cluster */ 186 case 's': /* service cluster */
165 check_type = CHECK_SERVICES; 187 result.config.check_type = CHECK_SERVICES;
166 break; 188 break;
167
168 case 'w': /* warning threshold */ 189 case 'w': /* warning threshold */
169 warn_threshold = strdup(optarg); 190 warn_threshold = strdup(optarg);
170 break; 191 break;
171
172 case 'c': /* warning threshold */ 192 case 'c': /* warning threshold */
173 crit_threshold = strdup(optarg); 193 crit_threshold = strdup(optarg);
174 break; 194 break;
175
176 case 'd': /* data values */ 195 case 'd': /* data values */
177 data_vals = (char *)strdup(optarg); 196 result.config.data_vals = strdup(optarg);
178 /* validate data */ 197 /* validate data */
179 for (ptr = data_vals; ptr != NULL; ptr += 2) { 198 for (char *ptr = result.config.data_vals; ptr != NULL; ptr += 2) {
180 if (ptr[0] < '0' || ptr[0] > '3') 199 if (ptr[0] < '0' || ptr[0] > '3') {
181 return ERROR; 200 result.errorcode = ERROR;
182 if (ptr[1] == '\0') 201 return result;
202 }
203 if (ptr[1] == '\0') {
183 break; 204 break;
184 if (ptr[1] != ',') 205 }
185 return ERROR; 206 if (ptr[1] != ',') {
207 result.errorcode = ERROR;
208 return result;
209 }
186 } 210 }
187 break; 211 break;
188
189 case 'l': /* text label */ 212 case 'l': /* text label */
190 label = (char *)strdup(optarg); 213 result.config.label = strdup(optarg);
191 break; 214 break;
192
193 case 'v': /* verbose */ 215 case 'v': /* verbose */
194 verbose++; 216 verbose++;
195 break; 217 break;
196
197 case 'V': /* version */ 218 case 'V': /* version */
198 print_revision(progname, NP_VERSION); 219 print_revision(progname, NP_VERSION);
199 exit(STATE_UNKNOWN); 220 exit(STATE_UNKNOWN);
200 break; 221 break;
201
202 case 'H': /* help */ 222 case 'H': /* help */
203 print_help(); 223 print_help();
204 exit(STATE_UNKNOWN); 224 exit(STATE_UNKNOWN);
205 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 }
206 233
234 result.config.output_format_is_set = true;
235 result.config.output_format = parser.output_format;
236 break;
237 }
207 default: 238 default:
208 return ERROR; 239 result.errorcode = ERROR;
240 return result;
209 break; 241 break;
210 } 242 }
211 } 243 }
212 244
213 if (data_vals == NULL) 245 if (result.config.data_vals == NULL) {
214 return ERROR; 246 result.errorcode = ERROR;
247 return result;
248 }
215 249
216 return OK; 250 set_thresholds(&result.config.thresholds, warn_threshold, crit_threshold);
251 return result;
217} 252}
218 253
219void print_help(void) { 254void print_help(void) {
@@ -247,6 +282,8 @@ void print_help(void) {
247 282
248 printf(UT_VERBOSE); 283 printf(UT_VERBOSE);
249 284
285 printf(UT_OUTPUT_FORMAT);
286
250 printf("\n"); 287 printf("\n");
251 printf("%s\n", _("Notes:")); 288 printf("%s\n", _("Notes:"));
252 printf(UT_THRESHOLDS_NOTES); 289 printf(UT_THRESHOLDS_NOTES);
@@ -254,7 +291,8 @@ void print_help(void) {
254 printf("\n"); 291 printf("\n");
255 printf("%s\n", _("Examples:")); 292 printf("%s\n", _("Examples:"));
256 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:");
257 printf(" %s\n", _("Will alert critical if there are 3 or more service data points in a non-OK")); 294 printf(" %s\n",
295 _("Will alert critical if there are 3 or more service data points in a non-OK"));
258 printf(" %s\n", _("state.")); 296 printf(" %s\n", _("state."));
259 297
260 printf(UT_SUPPORT); 298 printf(UT_SUPPORT);
diff --git a/plugins/check_cluster.d/config.h b/plugins/check_cluster.d/config.h
new file mode 100644
index 00000000..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 748201e8..fc704171 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -32,16 +32,23 @@
32 * 32 *
33 * 33 *
34 *****************************************************************************/ 34 *****************************************************************************/
35const char *progname = "check_curl";
36 35
36const char *progname = "check_curl";
37const char *copyright = "2006-2024"; 37const char *copyright = "2006-2024";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "check_curl.d/config.h"
41#include "states.h"
42#include "thresholds.h"
40#include <stdbool.h> 43#include <stdbool.h>
41#include <ctype.h> 44#include <ctype.h>
45#include "output.h"
46#include "perfdata.h"
42 47
48#include <assert.h>
43#include "common.h" 49#include "common.h"
44#include "utils.h" 50#include "utils.h"
51#include "./check_curl.d/check_curl_helpers.h"
45 52
46#ifndef LIBCURL_PROTOCOL_HTTP 53#ifndef LIBCURL_PROTOCOL_HTTP
47# error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense 54# error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense
@@ -50,8 +57,6 @@ const char *email = "devel@monitoring-plugins.org";
50#include "curl/curl.h" 57#include "curl/curl.h"
51#include "curl/easy.h" 58#include "curl/easy.h"
52 59
53#include "picohttpparser.h"
54
55#include "uriparser/Uri.h" 60#include "uriparser/Uri.h"
56 61
57#include <arpa/inet.h> 62#include <arpa/inet.h>
@@ -63,207 +68,62 @@ const char *email = "devel@monitoring-plugins.org";
63 68
64#include <netdb.h> 69#include <netdb.h>
65 70
66#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
67
68#define DEFAULT_BUFFER_SIZE 2048
69#define DEFAULT_SERVER_URL "/"
70#define HTTP_EXPECT "HTTP/"
71#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
72enum { 71enum {
73 MAX_IPV4_HOSTLENGTH = 255, 72 MAX_IPV4_HOSTLENGTH = 255,
74 HTTP_PORT = 80,
75 HTTPS_PORT = 443,
76 MAX_PORT = 65535,
77 DEFAULT_MAX_REDIRS = 15
78}; 73};
79 74
80enum { 75enum {
81 STICKY_NONE = 0, 76 REGS = 2,
82 STICKY_HOST = 1,
83 STICKY_PORT = 2
84};
85
86enum {
87 FOLLOW_HTTP_CURL = 0,
88 FOLLOW_LIBCURL = 1
89}; 77};
90 78
91/* for buffers for header and body */ 79#include "regex.h"
92typedef struct {
93 char *buf;
94 size_t buflen;
95 size_t bufsize;
96} curlhelp_write_curlbuf;
97 80
98/* for buffering the data sent in PUT */ 81// Globals
99typedef struct { 82int verbose = 0;
100 char *buf;
101 size_t buflen;
102 off_t pos;
103} curlhelp_read_curlbuf;
104 83
105/* for parsing the HTTP status line */ 84extern char errbuf[MAX_INPUT_BUFFER];
106typedef struct { 85extern bool is_openssl_callback;
107 int http_major; /* major version of the protocol, always 1 (HTTP/0.9 86extern bool add_sslctx_verify_fun;
108 * never reached the big internet most likely) */
109 int http_minor; /* minor version of the protocol, usually 0 or 1 */
110 int http_code; /* HTTP return code as in RFC 2145 */
111 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
112 * http://support.microsoft.com/kb/318380/en-us */
113 const char *msg; /* the human readable message */
114 char *first_line; /* a copy of the first line */
115} curlhelp_statusline;
116
117/* to know the underlying SSL library used by libcurl */
118typedef enum curlhelp_ssl_library {
119 CURLHELP_SSL_LIBRARY_UNKNOWN,
120 CURLHELP_SSL_LIBRARY_OPENSSL,
121 CURLHELP_SSL_LIBRARY_LIBRESSL,
122 CURLHELP_SSL_LIBRARY_GNUTLS,
123 CURLHELP_SSL_LIBRARY_NSS
124} curlhelp_ssl_library;
125 87
126enum {
127 REGS = 2,
128 MAX_RE_SIZE = 1024
129};
130#include "regex.h"
131static regex_t preg;
132static regmatch_t pmatch[REGS];
133static char regexp[MAX_RE_SIZE];
134static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
135static int errcode;
136static bool invert_regex = false;
137static int state_regex = STATE_CRITICAL;
138
139static char *server_address = NULL;
140static char *host_name = NULL;
141static char *server_url = 0;
142static struct curl_slist *server_ips = NULL;
143static bool specify_port = false;
144static unsigned short server_port = HTTP_PORT;
145static unsigned short virtual_port = 0;
146static int host_name_length;
147static char output_header_search[30] = "";
148static char output_string_search[30] = "";
149static char *warning_thresholds = NULL;
150static char *critical_thresholds = NULL;
151static int days_till_exp_warn, days_till_exp_crit;
152static thresholds *thlds;
153static char user_agent[DEFAULT_BUFFER_SIZE];
154static int verbose = 0;
155static bool show_extended_perfdata = false;
156static bool show_body = false;
157static int min_page_len = 0;
158static int max_page_len = 0;
159static int redir_depth = 0;
160static int max_depth = DEFAULT_MAX_REDIRS;
161static char *http_method = NULL;
162static char *http_post_data = NULL;
163static char *http_content_type = NULL;
164static CURL *curl;
165static bool curl_global_initialized = false;
166static bool curl_easy_initialized = false;
167static struct curl_slist *header_list = NULL;
168static bool body_buf_initialized = false;
169static curlhelp_write_curlbuf body_buf;
170static bool header_buf_initialized = false;
171static curlhelp_write_curlbuf header_buf;
172static bool status_line_initialized = false;
173static curlhelp_statusline status_line;
174static bool put_buf_initialized = false;
175static curlhelp_read_curlbuf put_buf;
176static char http_header[DEFAULT_BUFFER_SIZE];
177static long code;
178static long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
179static double total_time;
180static double time_connect;
181static double time_appconnect;
182static double time_headers;
183static double time_firstbyte;
184static char errbuf[MAX_INPUT_BUFFER];
185static CURLcode res;
186static char url[DEFAULT_BUFFER_SIZE];
187static char msg[DEFAULT_BUFFER_SIZE];
188static char perfstring[DEFAULT_BUFFER_SIZE];
189static char header_expect[MAX_INPUT_BUFFER] = "";
190static char string_expect[MAX_INPUT_BUFFER] = "";
191static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
192static int server_expect_yn = 0;
193static char user_auth[MAX_INPUT_BUFFER] = "";
194static char proxy_auth[MAX_INPUT_BUFFER] = "";
195static char **http_opt_headers;
196static int http_opt_headers_count = 0;
197static bool display_html = false;
198static int onredirect = STATE_OK;
199static int followmethod = FOLLOW_HTTP_CURL;
200static int followsticky = STICKY_NONE;
201static bool use_ssl = false;
202static bool check_cert = false;
203static bool continue_after_check_cert = false;
204typedef union {
205 struct curl_slist *to_info;
206 struct curl_certinfo *to_certinfo;
207} cert_ptr_union;
208static cert_ptr_union cert_ptr;
209static int ssl_version = CURL_SSLVERSION_DEFAULT;
210static char *client_cert = NULL;
211static char *client_privkey = NULL;
212static char *ca_cert = NULL;
213static bool verify_peer_and_host = false;
214static bool is_openssl_callback = false;
215static bool add_sslctx_verify_fun = false;
216#if defined(HAVE_SSL) && defined(USE_OPENSSL) 88#if defined(HAVE_SSL) && defined(USE_OPENSSL)
217static X509 *cert = NULL; 89static X509 *cert = NULL;
218#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 90#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
219static bool no_body = false; 91
220static int maximum_age = -1; 92typedef struct {
221static int address_family = AF_UNSPEC; 93 int errorcode;
222static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; 94 check_curl_config config;
223static int curl_http_version = CURL_HTTP_VERSION_NONE; 95} check_curl_config_wrapper;
224static bool automatic_decompression = false; 96static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
225static char *cookie_jar_file = NULL; 97
226static bool haproxy_protocol = false; 98static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState,
227 99 int redir_depth);
228static bool process_arguments(int /*argc*/, char ** /*argv*/); 100
229static void handle_curl_option_return_code(CURLcode res, const char *option); 101typedef struct {
230static int check_http(void); 102 int redir_depth;
231static void redir(curlhelp_write_curlbuf * /*header_buf*/); 103 check_curl_working_state working_state;
232static char *perfd_time(double elapsed_time); 104 int error_code;
233static char *perfd_time_connect(double elapsed_time_connect); 105 check_curl_global_state curl_state;
234static char *perfd_time_ssl(double elapsed_time_ssl); 106} redir_wrapper;
235static char *perfd_time_firstbyte(double elapsed_time_firstbyte); 107static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/,
236static char *perfd_time_headers(double elapsed_time_headers); 108 int redir_depth, check_curl_working_state working_state);
237static char *perfd_time_transfer(double elapsed_time_transfer); 109
238static char *perfd_size(int page_len);
239static void print_help(void); 110static void print_help(void);
240void print_usage(void); 111void print_usage(void);
112
241static void print_curl_version(void); 113static void print_curl_version(void);
242static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); 114
243static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); 115// typedef struct {
244static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); 116// int errorcode;
245static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); 117// } check_curl_evaluation_wrapper;
246static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); 118// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config,
247static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); 119// mp_check overall[static 1]) {}
248static curlhelp_ssl_library curlhelp_get_ssl_library(void);
249static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
250int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
251
252static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
253static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
254static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
255static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE]);
256static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf);
257 120
258#if defined(HAVE_SSL) && defined(USE_OPENSSL) 121#if defined(HAVE_SSL) && defined(USE_OPENSSL)
259int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); 122mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
123 int days_till_exp_crit);
260#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ 124#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
261 125
262static void test_file(char * /*path*/);
263
264int main(int argc, char **argv) { 126int main(int argc, char **argv) {
265 int result = STATE_UNKNOWN;
266
267 setlocale(LC_ALL, ""); 127 setlocale(LC_ALL, "");
268 bindtextdomain(PACKAGE, LOCALEDIR); 128 bindtextdomain(PACKAGE, LOCALEDIR);
269 textdomain(PACKAGE); 129 textdomain(PACKAGE);
@@ -271,24 +131,30 @@ int main(int argc, char **argv) {
271 /* Parse extra opts if any */ 131 /* Parse extra opts if any */
272 argv = np_extra_opts(&argc, argv, progname); 132 argv = np_extra_opts(&argc, argv, progname);
273 133
274 /* set defaults */
275 snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version());
276
277 /* parse arguments */ 134 /* parse arguments */
278 if (process_arguments(argc, argv) == false) 135 check_curl_config_wrapper tmp_config = process_arguments(argc, argv);
136 if (tmp_config.errorcode == ERROR) {
279 usage4(_("Could not parse arguments")); 137 usage4(_("Could not parse arguments"));
138 }
280 139
281 if (display_html) 140 const check_curl_config config = tmp_config.config;
282 printf("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", use_ssl ? "https" : "http", host_name ? host_name : server_address,
283 virtual_port ? virtual_port : server_port, server_url);
284 141
285 result = check_http(); 142 if (config.output_format_is_set) {
286 return result; 143 mp_set_format(config.output_format);
144 }
145
146 check_curl_working_state working_state = config.initial_config;
147
148 mp_check overall = mp_check_init();
149 mp_subcheck sc_test = check_http(config, working_state, 0);
150
151 mp_add_subcheck_to_check(&overall, sc_test);
152
153 mp_exit(overall);
287} 154}
288 155
289#ifdef HAVE_SSL 156#ifdef HAVE_SSL
290# ifdef USE_OPENSSL 157# ifdef USE_OPENSSL
291
292int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { 158int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
293 (void)preverify_ok; 159 (void)preverify_ok;
294 /* TODO: we get all certificates of the chain, so which ones 160 /* TODO: we get all certificates of the chain, so which ones
@@ -301,19 +167,21 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
301# endif 167# endif
302 if (verbose >= 2) { 168 if (verbose >= 2) {
303 puts("* SSL verify callback with certificate:"); 169 puts("* SSL verify callback with certificate:");
304 X509_NAME *subject;
305 X509_NAME *issuer;
306 printf("* issuer:\n"); 170 printf("* issuer:\n");
307 issuer = X509_get_issuer_name(cert); 171 X509_NAME *issuer = X509_get_issuer_name(cert);
308 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); 172 X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE);
309 printf("* curl verify_callback:\n* subject:\n"); 173 printf("* curl verify_callback:\n* subject:\n");
310 subject = X509_get_subject_name(cert); 174 X509_NAME *subject = X509_get_subject_name(cert);
311 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); 175 X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE);
312 puts(""); 176 puts("");
313 } 177 }
314 return 1; 178 return 1;
315} 179}
180# endif /* USE_OPENSSL */
181#endif /* HAVE_SSL */
316 182
183#ifdef HAVE_SSL
184# ifdef USE_OPENSSL
317CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { 185CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
318 (void)curl; // ignore unused parameter 186 (void)curl; // ignore unused parameter
319 (void)parm; // ignore unused parameter 187 (void)parm; // ignore unused parameter
@@ -330,877 +198,503 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) {
330 198
331 return CURLE_OK; 199 return CURLE_OK;
332} 200}
333
334# endif /* USE_OPENSSL */ 201# endif /* USE_OPENSSL */
335#endif /* HAVE_SSL */ 202#endif /* HAVE_SSL */
336 203
337/* returns a string "HTTP/1.x" or "HTTP/2" */ 204mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState,
338static char *string_statuscode(int major, int minor) { 205 int redir_depth) {
339 static char buf[10];
340
341 switch (major) {
342 case 1:
343 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
344 break;
345 case 2:
346 case 3:
347 snprintf(buf, sizeof(buf), "HTTP/%d", major);
348 break;
349 default:
350 /* assuming here HTTP/N with N>=4 */
351 snprintf(buf, sizeof(buf), "HTTP/%d", major);
352 break;
353 }
354 206
355 return buf; 207 // =======================
356} 208 // Initialisation for curl
209 // =======================
210 check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl(
211 config.curl_config, workingState, config.check_cert, config.on_redirect_dependent,
212 config.followmethod, config.max_depth);
357 213
358/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 214 check_curl_global_state curl_state = conf_curl_struct.curl_state;
359static int expected_statuscode(const char *reply, const char *statuscodes) { 215 workingState = conf_curl_struct.working_state;
360 char *expected;
361 char *code;
362 int result = 0;
363 216
364 if ((expected = strdup(statuscodes)) == NULL) 217 mp_subcheck sc_result = mp_subcheck_init();
365 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
366 218
367 for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) 219 char *url = fmt_url(workingState);
368 if (strstr(reply, code) != NULL) { 220 xasprintf(&sc_result.output, "Testing %s", url);
369 result = 1; 221 // TODO add some output here URL or something
370 break; 222 free(url);
371 }
372
373 free(expected);
374 return result;
375}
376 223
377void handle_curl_option_return_code(CURLcode res, const char *option) { 224 // ==============
378 if (res != CURLE_OK) { 225 // do the request
379 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, 226 // ==============
380 curl_easy_strerror(res)); 227 CURLcode res = curl_easy_perform(curl_state.curl);
381 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
382 }
383}
384
385int lookup_host(const char *host, char *buf, size_t buflen) {
386 struct addrinfo hints, *res, *result;
387 char addrstr[100];
388 size_t addrstr_len;
389 int errcode;
390 void *ptr = {0};
391 size_t buflen_remaining = buflen - 1;
392
393 memset(&hints, 0, sizeof(hints));
394 hints.ai_family = address_family;
395 hints.ai_socktype = SOCK_STREAM;
396 hints.ai_flags |= AI_CANONNAME;
397
398 errcode = getaddrinfo(host, NULL, &hints, &result);
399 if (errcode != 0)
400 return errcode;
401
402 strcpy(buf, "");
403 res = result;
404
405 while (res) {
406 switch (res->ai_family) {
407 case AF_INET:
408 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
409 break;
410 case AF_INET6:
411 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
412 break;
413 }
414
415 inet_ntop(res->ai_family, ptr, addrstr, 100);
416 if (verbose >= 1) {
417 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr);
418 }
419
420 // Append all IPs to buf as a comma-separated string
421 addrstr_len = strlen(addrstr);
422 if (buflen_remaining > addrstr_len + 1) {
423 if (buf[0] != '\0') {
424 strncat(buf, ",", buflen_remaining);
425 buflen_remaining -= 1;
426 }
427 strncat(buf, addrstr, buflen_remaining);
428 buflen_remaining -= addrstr_len;
429 }
430
431 res = res->ai_next;
432 }
433
434 freeaddrinfo(result);
435
436 return 0;
437}
438
439static void cleanup(void) {
440 if (status_line_initialized)
441 curlhelp_free_statusline(&status_line);
442 status_line_initialized = false;
443 if (curl_easy_initialized)
444 curl_easy_cleanup(curl);
445 curl_easy_initialized = false;
446 if (curl_global_initialized)
447 curl_global_cleanup();
448 curl_global_initialized = false;
449 if (body_buf_initialized)
450 curlhelp_freewritebuffer(&body_buf);
451 body_buf_initialized = false;
452 if (header_buf_initialized)
453 curlhelp_freewritebuffer(&header_buf);
454 header_buf_initialized = false;
455 if (put_buf_initialized)
456 curlhelp_freereadbuffer(&put_buf);
457 put_buf_initialized = false;
458}
459 228
460int check_http(void) { 229 if (verbose >= 2 && workingState.http_post_data) {
461 int result = STATE_OK; 230 printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data);
462 int result_ssl = STATE_OK;
463 int page_len = 0;
464 int i;
465 char *force_host_header = NULL;
466 struct curl_slist *host = NULL;
467 char addrstr[DEFAULT_BUFFER_SIZE / 2];
468 char dnscache[DEFAULT_BUFFER_SIZE];
469
470 /* initialize curl */
471 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
472 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
473 curl_global_initialized = true;
474
475 if ((curl = curl_easy_init()) == NULL) {
476 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
477 } 231 }
478 curl_easy_initialized = true;
479 232
480 /* register cleanup function to shut down libcurl properly */ 233 mp_subcheck sc_curl = mp_subcheck_init();
481 atexit(cleanup);
482 234
483 if (verbose >= 1) 235 /* Curl errors, result in critical Nagios state */
484 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); 236 if (res != CURLE_OK) {
485 237 xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"),
486 /* print everything on stdout like check_http would do */ 238 res, errbuf[0] ? errbuf : curl_easy_strerror(res));
487 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); 239 sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL);
488 240 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
489 if (automatic_decompression) 241 return sc_result;
490#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
491 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING");
492#else
493 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
494#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
495
496 /* initialize buffer for body of the answer */
497 if (curlhelp_initwritebuffer(&body_buf) < 0)
498 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
499 body_buf_initialized = true;
500 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback),
501 "CURLOPT_WRITEFUNCTION");
502 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
503
504 /* initialize buffer for header of the answer */
505 if (curlhelp_initwritebuffer(&header_buf) < 0)
506 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
507 header_buf_initialized = true;
508 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback),
509 "CURLOPT_HEADERFUNCTION");
510 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
511
512 /* set the error buffer */
513 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
514
515 /* set timeouts */
516 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
517 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
518
519 /* enable haproxy protocol */
520 if (haproxy_protocol) {
521 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL");
522 } 242 }
523 243
524 // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we 244 /* get status line of answer, check sanity of HTTP code */
525 // use the host_name later on to make SNI happy 245 if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) {
526 if (use_ssl && host_name != NULL) { 246 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
527 if ((res = lookup_host(server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { 247 /* we cannot know the major/minor version here for sure as we cannot parse the first
528 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), server_address, res, 248 * line */
529 gai_strerror(res)); 249 xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line");
530 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 250 return sc_result;
531 }
532 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr);
533 host = curl_slist_append(NULL, dnscache);
534 curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
535 if (verbose >= 1)
536 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
537 } 251 }
538 252
539 // If server_address is an IPv6 address it must be surround by square brackets 253 curl_state.status_line_initialized = true;
540 struct in6_addr tmp_in_addr;
541 if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) {
542 char *new_server_address = malloc(strlen(server_address) + 3);
543 if (new_server_address == NULL) {
544 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
545 }
546 snprintf(new_server_address, strlen(server_address) + 3, "[%s]", server_address);
547 free(server_address);
548 server_address = new_server_address;
549 }
550 254
551 /* compose URL: use the address we want to connect to, set Host: header later */ 255 size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf);
552 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http",
553 (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url);
554
555 if (verbose >= 1)
556 printf("* curl CURLOPT_URL: %s\n", url);
557 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL");
558
559 /* extract proxy information for legacy proxy https requests */
560 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
561 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
562 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
563 if (verbose >= 2)
564 printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
565 http_method = "GET";
566 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL");
567 }
568 256
569 /* disable body for HEAD request */ 257 double total_time;
570 if (http_method && !strcmp(http_method, "HEAD")) { 258 handle_curl_option_return_code(
571 no_body = true; 259 curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time),
572 } 260 "CURLINFO_TOTAL_TIME");
573 261
574 /* set HTTP protocol version */ 262 xasprintf(
575 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION"); 263 &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time",
576 264 string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor),
577 /* set HTTP method */ 265 curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time);
578 if (http_method) { 266 sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK);
579 if (!strcmp(http_method, "POST")) 267 mp_add_subcheck_to_subcheck(&sc_result, sc_curl);
580 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST");
581 else if (!strcmp(http_method, "PUT"))
582 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
583 else
584 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
585 }
586 268
587 /* check if Host header is explicitly set in options */ 269 // ==========
588 if (http_opt_headers_count) { 270 // Evaluation
589 for (i = 0; i < http_opt_headers_count; i++) { 271 // ==========
590 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
591 force_host_header = http_opt_headers[i];
592 }
593 }
594 }
595
596 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */
597 if (host_name != NULL && force_host_header == NULL) {
598 if ((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
599 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
600 } else {
601 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
602 }
603 header_list = curl_slist_append(header_list, http_header);
604 }
605
606 /* always close connection, be nice to servers */
607 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
608 header_list = curl_slist_append(header_list, http_header);
609
610 /* attach additional headers supplied by the user */
611 /* optionally send any other header tag */
612 if (http_opt_headers_count) {
613 for (i = 0; i < http_opt_headers_count; i++) {
614 header_list = curl_slist_append(header_list, http_opt_headers[i]);
615 }
616 /* This cannot be free'd here because a redirection will then try to access this and segfault */
617 /* Covered in a testcase in tests/check_http.t */
618 /* free(http_opt_headers); */
619 }
620
621 /* set HTTP headers */
622 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER");
623 272
624#ifdef LIBCURL_FEATURE_SSL 273#ifdef LIBCURL_FEATURE_SSL
274 if (workingState.use_ssl && config.check_cert) {
275 mp_subcheck sc_certificate = check_curl_certificate_checks(
276 curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit);
625 277
626 /* set SSL version, warn about insecure or unsupported versions */ 278 mp_add_subcheck_to_subcheck(&sc_result, sc_certificate);
627 if (use_ssl) { 279 if (!config.continue_after_check_cert) {
628 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); 280 return sc_result;
629 }
630
631 /* client certificate and key to present to server (SSL) */
632 if (client_cert)
633 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
634 if (client_privkey)
635 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
636 if (ca_cert) {
637 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
638 }
639 if (ca_cert || verify_peer_and_host) {
640 /* per default if we have a CA verify both the peer and the
641 * hostname in the certificate, can be switched off later */
642 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
643 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
644 } else {
645 /* backward-compatible behaviour, be tolerant in checks
646 * TODO: depending on more options have aspects we want
647 * to be less tolerant about ssl verfications
648 */
649 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
650 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
651 }
652
653 /* detect SSL library used by libcurl */
654 ssl_library = curlhelp_get_ssl_library();
655
656 /* try hard to get a stack of certificates to verify against */
657 if (check_cert) {
658# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
659 /* inform curl to report back certificates */
660 switch (ssl_library) {
661 case CURLHELP_SSL_LIBRARY_OPENSSL:
662 case CURLHELP_SSL_LIBRARY_LIBRESSL:
663 /* set callback to extract certificate with OpenSSL context function (works with
664 * OpenSSL-style libraries only!) */
665# ifdef USE_OPENSSL
666 /* libcurl and monitoring plugins built with OpenSSL, good */
667 add_sslctx_verify_fun = true;
668 is_openssl_callback = true;
669# endif /* USE_OPENSSL */
670 /* libcurl is built with OpenSSL, monitoring plugins, so falling
671 * back to manually extracting certificate information */
672 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
673 break;
674
675 case CURLHELP_SSL_LIBRARY_NSS:
676# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
677 /* NSS: support for CERTINFO is implemented since 7.34.0 */
678 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
679# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
680 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n",
681 curlhelp_get_ssl_library_string(ssl_library));
682# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
683 break;
684
685 case CURLHELP_SSL_LIBRARY_GNUTLS:
686# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
687 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
688 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
689# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
690 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n",
691 curlhelp_get_ssl_library_string(ssl_library));
692# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
693 break;
694
695 case CURLHELP_SSL_LIBRARY_UNKNOWN:
696 default:
697 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n",
698 curlhelp_get_ssl_library_string(ssl_library));
699 break;
700 } 281 }
701# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
702 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
703 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
704 add_sslctx_verify_fun = true;
705 else
706 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
707 "too old and has no CURLOPT_CERTINFO)\n");
708# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
709 } 282 }
710
711# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
712 // ssl ctx function is not available with all ssl backends
713 if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION)
714 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
715# endif
716
717#endif /* LIBCURL_FEATURE_SSL */
718
719 /* set default or user-given user agent identification */
720 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT");
721
722 /* proxy-authentication */
723 if (strcmp(proxy_auth, ""))
724 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
725
726 /* authentication */
727 if (strcmp(user_auth, ""))
728 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
729
730 /* TODO: parameter auth method, bitfield of following methods:
731 * CURLAUTH_BASIC (default)
732 * CURLAUTH_DIGEST
733 * CURLAUTH_DIGEST_IE
734 * CURLAUTH_NEGOTIATE
735 * CURLAUTH_NTLM
736 * CURLAUTH_NTLM_WB
737 *
738 * convenience tokens for typical sets of methods:
739 * CURLAUTH_ANYSAFE: most secure, without BASIC
740 * or CURLAUTH_ANY: most secure, even BASIC if necessary
741 *
742 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
743 */
744
745 /* handle redirections */
746 if (onredirect == STATE_DEPENDENT) {
747 if (followmethod == FOLLOW_LIBCURL) {
748 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
749
750 /* default -1 is infinite, not good, could lead to zombie plugins!
751 Setting it to one bigger than maximal limit to handle errors nicely below
752 */
753 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS");
754
755 /* for now allow only http and https (we are a http(s) check plugin in the end) */
756#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
757 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
758 "CURLOPT_REDIR_PROTOCOLS_STR");
759#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
760 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS),
761 "CURLOPT_REDIRECT_PROTOCOLS");
762#endif 283#endif
763 284
764 /* TODO: handle the following aspects of redirection, make them 285 /* we got the data and we executed the request in a given time, so we can append
765 * command line options too later: 286 * performance data to the answer always
766 CURLOPT_POSTREDIR: method switch 287 */
767 CURLINFO_REDIRECT_URL: custom redirect option
768 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
769 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
770 */
771 } else {
772 /* old style redirection is handled below */
773 }
774 }
775
776 /* no-body */
777 if (no_body)
778 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
779
780 /* IPv4 or IPv6 forced DNS resolution */
781 if (address_family == AF_UNSPEC)
782 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
783 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
784 else if (address_family == AF_INET)
785 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
786 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
787#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
788 else if (address_family == AF_INET6)
789 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
790 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
791#endif
792 288
793 /* either send http POST data (any data, not only POST)*/ 289 // total time the query took
794 if (!strcmp(http_method, "POST") || !strcmp(http_method, "PUT")) { 290 mp_perfdata pd_total_time = perfdata_init();
795 /* set content of payload for POST and PUT */ 291 mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time);
796 if (http_content_type) { 292 pd_total_time.value = pd_val_total_time;
797 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type); 293 pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds);
798 header_list = curl_slist_append(header_list, http_header); 294 pd_total_time.label = "time";
799 } 295 pd_total_time.uom = "s";
800 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string 296
801 * in case of no POST/PUT data */ 297 mp_subcheck sc_total_time = mp_subcheck_init();
802 if (!http_post_data) 298 sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time));
803 http_post_data = ""; 299 xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time);
804 if (!strcmp(http_method, "POST")) { 300 mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time);
805 /* POST method, set payload with CURLOPT_POSTFIELDS */ 301
806 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); 302 mp_add_subcheck_to_subcheck(&sc_result, sc_total_time);
807 } else if (!strcmp(http_method, "PUT")) { 303
808 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), 304 if (config.show_extended_perfdata) {
809 "CURLOPT_READFUNCTION"); 305 // overall connection time
810 if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) 306 mp_perfdata pd_time_connect = perfdata_init();
811 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); 307 double time_connect;
812 put_buf_initialized = true; 308 handle_curl_option_return_code(
813 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); 309 curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect),
814 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), 310 "CURLINFO_CONNECT_TIME");
815 "CURLOPT_INFILESIZE"); 311
312 mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect);
313 pd_time_connect.value = pd_val_time_connect;
314 pd_time_connect.label = "time_connect";
315 pd_time_connect.uom = "s";
316 pd_time_connect = mp_set_pd_max_value(
317 pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout));
318
319 pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds);
320 mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect);
321
322 // application connection time, used to compute other timings
323 double time_appconnect;
324 handle_curl_option_return_code(
325 curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect),
326 "CURLINFO_APPCONNECT_TIME");
327
328 if (workingState.use_ssl) {
329 mp_perfdata pd_time_tls = perfdata_init();
330 {
331 mp_perfdata_value pd_val_time_tls =
332 mp_create_pd_value(time_appconnect - time_connect);
333
334 pd_time_tls.value = pd_val_time_tls;
335 }
336 pd_time_tls.label = "time_tls";
337 pd_time_tls.uom = "s";
338 mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls);
816 } 339 }
817 }
818
819 /* cookie handling */
820 if (cookie_jar_file != NULL) {
821 /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */
822 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE");
823 /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */
824 if (*cookie_jar_file)
825 handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR");
826 }
827
828 /* do the request */
829 res = curl_easy_perform(curl);
830 340
831 if (verbose >= 2 && http_post_data) 341 mp_perfdata pd_time_headers = perfdata_init();
832 printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); 342 {
343 double time_headers;
344 handle_curl_option_return_code(
345 curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers),
346 "CURLINFO_PRETRANSFER_TIME");
833 347
834 /* free header and server IP resolve lists, we don't need it anymore */ 348 mp_perfdata_value pd_val_time_headers =
835 curl_slist_free_all(header_list); 349 mp_create_pd_value(time_headers - time_appconnect);
836 header_list = NULL;
837 curl_slist_free_all(server_ips);
838 server_ips = NULL;
839 if (host) {
840 curl_slist_free_all(host);
841 host = NULL;
842 }
843 350
844 /* Curl errors, result in critical Nagios state */ 351 pd_time_headers.value = pd_val_time_headers;
845 if (res != CURLE_OK) { 352 }
846 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), server_port, 353 pd_time_headers.label = "time_headers";
847 res, errbuf[0] ? errbuf : curl_easy_strerror(res)); 354 pd_time_headers.uom = "s";
848 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); 355 mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers);
849 }
850 356
851 /* certificate checks */ 357 mp_perfdata pd_time_firstbyte = perfdata_init();
852#ifdef LIBCURL_FEATURE_SSL 358 double time_firstbyte;
853 if (use_ssl) { 359 handle_curl_option_return_code(
854 if (check_cert) { 360 curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
855 if (is_openssl_callback) { 361 "CURLINFO_STARTTRANSFER_TIME");
856# ifdef USE_OPENSSL
857 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
858 * and we actually have OpenSSL in the monitoring tools
859 */
860 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
861 if (!continue_after_check_cert) {
862 return result_ssl;
863 }
864# else /* USE_OPENSSL */
865 die(STATE_CRITICAL,
866 "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
867# endif /* USE_OPENSSL */
868 } else {
869 int i;
870 struct curl_slist *slist;
871 362
872 cert_ptr.to_info = NULL; 363 mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte);
873 res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); 364 pd_time_firstbyte.value = pd_val_time_firstbyte;
874 if (!res && cert_ptr.to_info) { 365 pd_time_firstbyte.label = "time_firstbyte";
875# ifdef USE_OPENSSL 366 pd_time_firstbyte.uom = "s";
876 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing 367 mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte);
877 * We only check the first certificate and assume it's the one of the server
878 */
879 const char *raw_cert = NULL;
880 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
881 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
882 if (verbose >= 2)
883 printf("%d ** %s\n", i, slist->data);
884 if (strncmp(slist->data, "Cert:", 5) == 0) {
885 raw_cert = &slist->data[5];
886 goto GOT_FIRST_CERT;
887 }
888 }
889 }
890 GOT_FIRST_CERT:
891 if (!raw_cert) {
892 snprintf(msg, DEFAULT_BUFFER_SIZE,
893 _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
894 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
895 }
896 BIO *cert_BIO = BIO_new(BIO_s_mem());
897 BIO_write(cert_BIO, raw_cert, strlen(raw_cert));
898 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
899 if (!cert) {
900 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error"));
901 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
902 }
903 BIO_free(cert_BIO);
904 result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
905 if (!continue_after_check_cert) {
906 return result_ssl;
907 }
908# else /* USE_OPENSSL */
909 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
910 * so we use the libcurl CURLINFO data
911 */
912 result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
913 if (!continue_after_check_cert) {
914 return result_ssl;
915 }
916# endif /* USE_OPENSSL */
917 } else {
918 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), res,
919 curl_easy_strerror(res));
920 die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
921 }
922 }
923 }
924 }
925#endif /* LIBCURL_FEATURE_SSL */
926 368
927 /* we got the data and we executed the request in a given time, so we can append 369 mp_perfdata pd_time_transfer = perfdata_init();
928 * performance data to the answer always 370 pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte);
929 */ 371 pd_time_transfer.label = "time_transfer";
930 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); 372 pd_time_transfer.uom = "s";
931 page_len = get_content_length(&header_buf, &body_buf); 373 mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer);
932 if (show_extended_perfdata) {
933 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
934 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
935 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
936 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte),
937 "CURLINFO_STARTTRANSFER_TIME");
938 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time), perfd_size(page_len),
939 perfd_time_connect(time_connect), use_ssl ? perfd_time_ssl(time_appconnect - time_connect) : "",
940 perfd_time_headers(time_headers - time_appconnect), perfd_time_firstbyte(time_firstbyte - time_headers),
941 perfd_time_transfer(total_time - time_firstbyte));
942 } else {
943 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time), perfd_size(page_len));
944 } 374 }
945 375
946 /* return a CRITICAL status if we couldn't read any data */ 376 /* return a CRITICAL status if we couldn't read any data */
947 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) 377 if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) {
948 die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); 378 sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL);
949 379 xasprintf(&sc_result.output, "No header received from host");
950 /* get status line of answer, check sanity of HTTP code */ 380 return sc_result;
951 if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) {
952 snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring);
953 /* we cannot know the major/minor version here for sure as we cannot parse the first line */
954 die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg);
955 } 381 }
956 status_line_initialized = true;
957 382
958 /* get result code from cURL */ 383 /* get result code from cURL */
959 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); 384 long httpReturnCode;
960 if (verbose >= 2) 385 handle_curl_option_return_code(
961 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); 386 curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode),
387 "CURLINFO_RESPONSE_CODE");
388 if (verbose >= 2) {
389 printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode);
390 }
962 391
963 /* print status line, header, body if verbose */ 392 /* print status line, header, body if verbose */
964 if (verbose >= 2) { 393 if (verbose >= 2) {
965 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (no_body ? " [[ skipped ]]" : body_buf.buf)); 394 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf,
395 (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf));
966 } 396 }
967 397
968 /* make sure the status line matches the response we are looking for */ 398 /* make sure the status line matches the response we are looking for */
969 if (!expected_statuscode(status_line.first_line, server_expect)) { 399 mp_subcheck sc_expect = mp_subcheck_init();
970 if (server_port == HTTP_PORT) 400 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK);
971 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); 401 if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) {
972 else 402 if (workingState.serverPort == HTTP_PORT) {
973 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, 403 xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"),
974 status_line.first_line); 404 curl_state.status_line->first_line);
975 die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); 405 } else {
406 xasprintf(&sc_expect.output,
407 _("Invalid HTTP response received from host on port %d: %s\n"),
408 workingState.serverPort, curl_state.status_line->first_line);
409 }
410 sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL);
411 } else {
412 xasprintf(&sc_expect.output, _("Status line output matched \"%s\""),
413 config.server_expect.string);
976 } 414 }
415 mp_add_subcheck_to_subcheck(&sc_result, sc_expect);
977 416
978 if (server_expect_yn) { 417 if (!config.server_expect.is_present) {
979 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
980 if (verbose)
981 printf("%s\n", msg);
982 result = STATE_OK;
983 } else {
984 /* illegal return codes result in a critical state */ 418 /* illegal return codes result in a critical state */
985 if (code >= 600 || code < 100) { 419 mp_subcheck sc_return_code = mp_subcheck_init();
986 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); 420 sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK);
987 /* server errors result in a critical state */ 421 xasprintf(&sc_return_code.output, "HTTP return code: %d",
988 } else if (code >= 500) { 422 curl_state.status_line->http_code);
989 result = STATE_CRITICAL; 423
424 if (httpReturnCode >= 600 || httpReturnCode < 100) {
425 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
426 xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"),
427 curl_state.status_line->http_code, curl_state.status_line->msg);
428 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
429 return sc_result;
430 }
431
432 // server errors result in a critical state
433 if (httpReturnCode >= 500) {
434 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL);
990 /* client errors result in a warning state */ 435 /* client errors result in a warning state */
991 } else if (code >= 400) { 436 } else if (httpReturnCode >= 400) {
992 result = STATE_WARNING; 437 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING);
993 /* check redirected page if specified */ 438 /* check redirected page if specified */
994 } else if (code >= 300) { 439 } else if (httpReturnCode >= 300) {
995 if (onredirect == STATE_DEPENDENT) { 440 if (config.on_redirect_dependent) {
996 if (followmethod == FOLLOW_LIBCURL) { 441 if (config.followmethod == FOLLOW_LIBCURL) {
997 code = status_line.http_code; 442 httpReturnCode = curl_state.status_line->http_code;
443 handle_curl_option_return_code(
444 curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth),
445 "CURLINFO_REDIRECT_COUNT");
446
447 if (verbose >= 2) {
448 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
449 }
450
451 mp_subcheck sc_redir_depth = mp_subcheck_init();
452 if (redir_depth > config.max_depth) {
453 xasprintf(&sc_redir_depth.output,
454 "maximum redirection depth %d exceeded in libcurl",
455 config.max_depth);
456 sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL);
457 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
458 return sc_result;
459 }
460 xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)",
461 redir_depth, config.max_depth);
462 mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth);
463
998 } else { 464 } else {
999 /* old check_http style redirection, if we come 465 /* old check_http style redirection, if we come
1000 * back here, we are in the same status as with 466 * back here, we are in the same status as with
1001 * the libcurl method 467 * the libcurl method
1002 */ 468 */
1003 redir(&header_buf); 469 redir_wrapper redir_result =
470 redir(curl_state.header_buf, config, redir_depth, workingState);
471 cleanup(curl_state);
472 mp_subcheck sc_redir =
473 check_http(config, redir_result.working_state, redir_result.redir_depth);
474 mp_add_subcheck_to_subcheck(&sc_result, sc_redir);
475
476 return sc_result;
1004 } 477 }
1005 } else { 478 } else {
1006 /* this is a specific code in the command line to 479 /* this is a specific code in the command line to
1007 * be returned when a redirection is encountered 480 * be returned when a redirection is encountered
1008 */ 481 */
482 sc_return_code =
483 mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state);
1009 } 484 }
1010 result = max_state_alt(onredirect, result);
1011 /* all other codes are considered ok */
1012 } else { 485 } else {
1013 result = STATE_OK; 486 sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK);
1014 } 487 }
1015 }
1016 488
1017 /* libcurl redirection internally, handle error states here */ 489 mp_add_subcheck_to_subcheck(&sc_result, sc_return_code);
1018 if (followmethod == FOLLOW_LIBCURL) {
1019 handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
1020 if (verbose >= 2)
1021 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
1022 if (redir_depth > max_depth) {
1023 snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth);
1024 die(STATE_WARNING, "HTTP WARNING - %s", msg);
1025 }
1026 } 490 }
1027 491
1028 /* check status codes, set exit status accordingly */ 492 /* check status codes, set exit status accordingly */
1029 if (status_line.http_code != code) { 493 if (curl_state.status_line->http_code != httpReturnCode) {
1030 die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), 494 mp_subcheck sc_http_return_code_sanity = mp_subcheck_init();
1031 string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); 495 sc_http_return_code_sanity =
496 mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL);
497 xasprintf(&sc_http_return_code_sanity.output,
498 _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"),
499 string_statuscode(curl_state.status_line->http_major,
500 curl_state.status_line->http_minor),
501 curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode);
502
503 mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity);
504 return sc_result;
1032 } 505 }
1033 506
1034 if (maximum_age >= 0) { 507 if (config.maximum_age >= 0) {
1035 result = max_state_alt(check_document_dates(&header_buf, &msg), result); 508 mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age);
509 mp_add_subcheck_to_subcheck(&sc_result, sc_max_age);
1036 } 510 }
1037 511
1038 /* Page and Header content checks go here */ 512 /* Page and Header content checks go here */
513 if (strlen(config.header_expect)) {
514 mp_subcheck sc_header_expect = mp_subcheck_init();
515 sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK);
516 xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect);
1039 517
1040 if (strlen(header_expect)) { 518 if (!strstr(curl_state.header_buf->buf, config.header_expect)) {
1041 if (!strstr(header_buf.buf, header_expect)) { 519 char output_header_search[30] = "";
1042 520 strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search));
1043 strncpy(&output_header_search[0], header_expect, sizeof(output_header_search));
1044 521
1045 if (output_header_search[sizeof(output_header_search) - 1] != '\0') { 522 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1046 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); 523 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1047 } 524 }
1048 525
1049 char tmp[DEFAULT_BUFFER_SIZE]; 526 xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "),
1050 527 output_header_search, workingState.use_ssl ? "https" : "http",
1051 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, 528 workingState.host_name ? workingState.host_name : workingState.server_address,
1052 use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 529 workingState.serverPort, workingState.server_url);
1053 530
1054 strcpy(msg, tmp); 531 sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL);
1055
1056 result = STATE_CRITICAL;
1057 } 532 }
533
534 mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect);
1058 } 535 }
1059 536
1060 if (strlen(string_expect)) { 537 if (strlen(config.string_expect)) {
1061 if (!strstr(body_buf.buf, string_expect)) { 538 mp_subcheck sc_string_expect = mp_subcheck_init();
539 sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK);
540 xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect);
1062 541
1063 strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); 542 if (!strstr(curl_state.body_buf->buf, config.string_expect)) {
543 char output_string_search[30] = "";
544 strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search));
1064 545
1065 if (output_string_search[sizeof(output_string_search) - 1] != '\0') { 546 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1066 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); 547 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
1067 } 548 }
1068 549
1069 char tmp[DEFAULT_BUFFER_SIZE]; 550 xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "),
1070 551 output_string_search, workingState.use_ssl ? "https" : "http",
1071 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, 552 workingState.host_name ? workingState.host_name : workingState.server_address,
1072 use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 553 workingState.serverPort, workingState.server_url);
1073
1074 strcpy(msg, tmp);
1075 554
1076 result = STATE_CRITICAL; 555 sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL);
1077 } 556 }
557
558 mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect);
1078 } 559 }
1079 560
1080 if (strlen(regexp)) { 561 if (strlen(config.regexp)) {
1081 errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); 562 mp_subcheck sc_body_regex = mp_subcheck_init();
1082 if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) { 563 xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp);
1083 /* OK - No-op to avoid changing the logic around it */ 564 regmatch_t pmatch[REGS];
1084 result = max_state_alt(STATE_OK, result);
1085 } else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) {
1086 if (!invert_regex) {
1087 char tmp[DEFAULT_BUFFER_SIZE];
1088 565
1089 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); 566 int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0);
1090 strcpy(msg, tmp);
1091 567
568 if (errcode == 0) {
569 // got a match
570 if (config.invert_regex) {
571 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
1092 } else { 572 } else {
1093 char tmp[DEFAULT_BUFFER_SIZE]; 573 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
574 }
575 } else if (errcode == REG_NOMATCH) {
576 // got no match
577 xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output);
1094 578
1095 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); 579 if (config.invert_regex) {
1096 strcpy(msg, tmp); 580 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK);
581 } else {
582 sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex);
1097 } 583 }
1098 result = state_regex;
1099 } else { 584 } else {
1100 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 585 // error in regexec
1101 586 char error_buffer[DEFAULT_BUFFER_SIZE];
1102 char tmp[DEFAULT_BUFFER_SIZE]; 587 regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE);
1103 588 xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer);
1104 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); 589 sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN);
1105 strcpy(msg, tmp);
1106 result = STATE_UNKNOWN;
1107 } 590 }
591
592 mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex);
1108 } 593 }
1109 594
1110 /* make sure the page is of an appropriate size */ 595 // size a.k.a. page length
1111 if ((max_page_len > 0) && (page_len > max_page_len)) { 596 mp_perfdata pd_page_length = perfdata_init();
1112 char tmp[DEFAULT_BUFFER_SIZE]; 597 mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len);
598 pd_page_length.value = pd_val_page_length;
599 pd_page_length.label = "size";
600 pd_page_length.uom = "B";
601 pd_page_length.min = mp_create_pd_value(0);
602 pd_page_length.warn = config.page_length_limits;
603 pd_page_length.warn_present = true;
1113 604
1114 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); 605 /* make sure the page is of an appropriate size */
606 if (config.page_length_limits_is_set) {
607 mp_thresholds page_length_threshold = mp_thresholds_init();
608 page_length_threshold.warning = config.page_length_limits;
609 page_length_threshold.warning_is_set = true;
1115 610
1116 strcpy(msg, tmp); 611 pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold);
1117 612
1118 result = max_state_alt(STATE_WARNING, result); 613 mp_subcheck sc_page_length = mp_subcheck_init();
1119 614
1120 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 615 mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length);
1121 char tmp[DEFAULT_BUFFER_SIZE];
1122 616
1123 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); 617 mp_state_enum tmp_state = mp_get_pd_status(pd_page_length);
1124 strcpy(msg, tmp); 618 sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state);
1125 result = max_state_alt(STATE_WARNING, result);
1126 }
1127 619
1128 /* -w, -c: check warning and critical level */ 620 switch (tmp_state) {
1129 result = max_state_alt(get_status(total_time, thlds), result); 621 case STATE_CRITICAL:
622 case STATE_WARNING:
623 xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len);
624 break;
625 case STATE_OK:
626 xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len);
627 break;
628 default:
629 assert(false);
630 }
1130 631
1131 /* Cut-off trailing characters */ 632 mp_add_subcheck_to_subcheck(&sc_result, sc_page_length);
1132 if (strlen(msg) >= 2) {
1133 if (msg[strlen(msg) - 2] == ',')
1134 msg[strlen(msg) - 2] = '\0';
1135 else
1136 msg[strlen(msg) - 3] = '\0';
1137 } 633 }
1138 634
1139 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ 635 return sc_result;
1140 die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result),
1141 string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg,
1142 strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (display_html ? "</A>" : ""), perfstring, (show_body ? body_buf.buf : ""),
1143 (show_body ? "\n" : ""));
1144
1145 return max_state_alt(result, result_ssl);
1146} 636}
1147 637
1148int uri_strcmp(const UriTextRangeA range, const char *s) { 638int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) {
1149 if (!range.first) 639 if (!range.first) {
1150 return -1; 640 return -1;
1151 if ((size_t)(range.afterLast - range.first) < strlen(s)) 641 }
642 if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) {
1152 return -1; 643 return -1;
1153 return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); 644 }
645 return strncmp(stringToCompare, range.first,
646 min((size_t)(range.afterLast - range.first), strlen(stringToCompare)));
1154} 647}
1155 648
1156char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { 649char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) {
1157 if (!range.first) 650 if (!range.first) {
1158 return "(null)"; 651 return "(null)";
652 }
1159 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); 653 strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first)));
1160 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; 654 buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0';
1161 buf[range.afterLast - range.first] = '\0'; 655 buf[range.afterLast - range.first] = '\0';
1162 return buf; 656 return buf;
1163} 657}
1164 658
1165void redir(curlhelp_write_curlbuf *header_buf) { 659redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config,
1166 char *location = NULL; 660 int redir_depth, check_curl_working_state working_state) {
1167 curlhelp_statusline status_line; 661 curlhelp_statusline status_line;
1168 struct phr_header headers[255]; 662 struct phr_header headers[255];
1169 size_t nof_headers = 255;
1170 size_t msglen; 663 size_t msglen;
1171 char buf[DEFAULT_BUFFER_SIZE]; 664 size_t nof_headers = 255;
1172 char ipstr[INET_ADDR_MAX_SIZE]; 665 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
1173 int new_port; 666 &status_line.http_minor, &status_line.http_code, &status_line.msg,
1174 char *new_host; 667 &msglen, headers, &nof_headers, 0);
1175 char *new_url;
1176
1177 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
1178 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
1179 668
1180 if (res == -1) { 669 if (res == -1) {
1181 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); 670 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
1182 } 671 }
1183 672
1184 location = get_header_value(headers, nof_headers, "location"); 673 char *location = get_header_value(headers, nof_headers, "location");
1185 674
1186 if (verbose >= 2) 675 if (verbose >= 2) {
1187 printf(_("* Seen redirect location %s\n"), location); 676 printf(_("* Seen redirect location %s\n"), location);
677 }
1188 678
1189 if (++redir_depth > max_depth) 679 if (++redir_depth > config.max_depth) {
1190 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, 680 die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"),
1191 (display_html ? "</A>" : "")); 681 config.max_depth, location);
682 }
1192 683
1193 UriParserStateA state; 684 UriParserStateA state;
1194 UriUriA uri; 685 UriUriA uri;
1195 state.uri = &uri; 686 state.uri = &uri;
1196 if (uriParseUriA(&state, location) != URI_SUCCESS) { 687 if (uriParseUriA(&state, location) != URI_SUCCESS) {
1197 if (state.errorCode == URI_ERROR_SYNTAX) { 688 if (state.errorCode == URI_ERROR_SYNTAX) {
1198 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, (display_html ? "</A>" : "")); 689 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"),
690 location);
1199 } else if (state.errorCode == URI_ERROR_MALLOC) { 691 } else if (state.errorCode == URI_ERROR_MALLOC) {
1200 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 692 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1201 } 693 }
1202 } 694 }
1203 695
696 char ipstr[INET_ADDR_MAX_SIZE];
697 char buf[DEFAULT_BUFFER_SIZE];
1204 if (verbose >= 2) { 698 if (verbose >= 2) {
1205 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); 699 printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE));
1206 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); 700 printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
@@ -1215,9 +709,9 @@ void redir(curlhelp_write_curlbuf *header_buf) {
1215 } 709 }
1216 if (uri.pathHead) { 710 if (uri.pathHead) {
1217 printf(_("** path: ")); 711 printf(_("** path: "));
1218 const UriPathSegmentA *p = uri.pathHead; 712 for (UriPathSegmentA *path_segment = uri.pathHead; path_segment;
1219 for (; p; p = p->next) { 713 path_segment = path_segment->next) {
1220 printf("/%s", uri_string(p->text, buf, DEFAULT_BUFFER_SIZE)); 714 printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE));
1221 } 715 }
1222 puts(""); 716 puts("");
1223 } 717 }
@@ -1230,99 +724,104 @@ void redir(curlhelp_write_curlbuf *header_buf) {
1230 } 724 }
1231 725
1232 if (uri.scheme.first) { 726 if (uri.scheme.first) {
1233 if (!uri_strcmp(uri.scheme, "https")) 727 working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https"));
1234 use_ssl = true;
1235 else
1236 use_ssl = false;
1237 } 728 }
1238 729
1239 /* we do a sloppy test here only, because uriparser would have failed 730 /* we do a sloppy test here only, because uriparser would have failed
1240 * above, if the port would be invalid, we just check for MAX_PORT 731 * above, if the port would be invalid, we just check for MAX_PORT
1241 */ 732 */
733 int new_port;
1242 if (uri.portText.first) { 734 if (uri.portText.first) {
1243 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); 735 new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE));
1244 } else { 736 } else {
1245 new_port = HTTP_PORT; 737 new_port = HTTP_PORT;
1246 if (use_ssl) 738 if (working_state.use_ssl) {
1247 new_port = HTTPS_PORT; 739 new_port = HTTPS_PORT;
740 }
741 }
742 if (new_port > MAX_PORT) {
743 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT,
744 location);
1248 } 745 }
1249 if (new_port > MAX_PORT)
1250 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "</A>" : "");
1251 746
1252 /* by RFC 7231 relative URLs in Location should be taken relative to 747 /* by RFC 7231 relative URLs in Location should be taken relative to
1253 * the original URL, so we try to form a new absolute URL here 748 * the original URL, so we try to form a new absolute URL here
1254 */ 749 */
750 char *new_host;
1255 if (!uri.scheme.first && !uri.hostText.first) { 751 if (!uri.scheme.first && !uri.hostText.first) {
1256 new_host = strdup(host_name ? host_name : server_address); 752 new_host = strdup(working_state.host_name ? working_state.host_name
1257 new_port = server_port; 753 : working_state.server_address);
1258 if (use_ssl) 754 new_port = working_state.serverPort;
755 if (working_state.use_ssl) {
1259 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); 756 uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE);
757 }
1260 } else { 758 } else {
1261 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); 759 new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1262 } 760 }
1263 761
1264 /* compose new path */ 762 /* compose new path */
1265 /* TODO: handle fragments and query part of URL */ 763 /* TODO: handle fragments and query part of URL */
1266 new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); 764 char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE);
1267 if (uri.pathHead) { 765 if (uri.pathHead) {
1268 const UriPathSegmentA *p = uri.pathHead; 766 for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment;
1269 for (; p; p = p->next) { 767 pathSegment = pathSegment->next) {
1270 strncat(new_url, "/", DEFAULT_BUFFER_SIZE); 768 strncat(new_url, "/", DEFAULT_BUFFER_SIZE);
1271 strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); 769 strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE),
770 DEFAULT_BUFFER_SIZE - 1);
1272 } 771 }
1273 } 772 }
1274 773
1275 if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && 774 if (working_state.serverPort == new_port &&
1276 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) 775 !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1277 die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", 776 (working_state.host_name &&
1278 new_host, new_port, new_url, (display_html ? "</A>" : "")); 777 !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
778 !strcmp(working_state.server_url, new_url)) {
779 die(STATE_CRITICAL,
780 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"),
781 working_state.use_ssl ? "https" : "http", new_host, new_port, new_url);
782 }
1279 783
1280 /* set new values for redirected request */ 784 /* set new values for redirected request */
1281 785
1282 if (!(followsticky & STICKY_HOST)) { 786 if (!(config.followsticky & STICKY_HOST)) {
1283 free(server_address); 787 free(working_state.server_address);
1284 server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); 788 working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH);
1285 } 789 }
1286 if (!(followsticky & STICKY_PORT)) { 790 if (!(config.followsticky & STICKY_PORT)) {
1287 server_port = (unsigned short)new_port; 791 working_state.serverPort = (unsigned short)new_port;
1288 } 792 }
1289 793
1290 free(host_name); 794 free(working_state.host_name);
1291 host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); 795 working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH);
1292 796
1293 /* reset virtual port */ 797 /* reset virtual port */
1294 virtual_port = server_port; 798 working_state.virtualPort = working_state.serverPort;
1295 799
1296 free(new_host); 800 free(new_host);
1297 free(server_url); 801 free(working_state.server_url);
1298 server_url = new_url; 802 working_state.server_url = new_url;
1299 803
1300 uriFreeUriMembersA(&uri); 804 uriFreeUriMembersA(&uri);
1301 805
1302 if (verbose) 806 if (verbose) {
1303 printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, 807 printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http",
1304 server_url); 808 working_state.host_name ? working_state.host_name : working_state.server_address,
809 working_state.serverPort, working_state.server_url);
810 }
1305 811
1306 /* TODO: the hash component MUST be taken from the original URL and 812 /* TODO: the hash component MUST be taken from the original URL and
1307 * attached to the URL in Location 813 * attached to the URL in Location
1308 */ 814 */
1309 815
1310 cleanup(); 816 redir_wrapper result = {
1311 check_http(); 817 .redir_depth = redir_depth,
1312} 818 .working_state = working_state,
1313 819 .error_code = OK,
1314/* check whether a file exists */ 820 };
1315void test_file(char *path) { 821 return result;
1316 if (access(path, R_OK) == 0)
1317 return;
1318 usage2(_("file does not exist or is not readable"), path);
1319} 822}
1320 823
1321bool process_arguments(int argc, char **argv) { 824check_curl_config_wrapper process_arguments(int argc, char **argv) {
1322 char *p;
1323 int c = 1;
1324 char *temp;
1325
1326 enum { 825 enum {
1327 INVERT_REGEX = CHAR_MAX + 1, 826 INVERT_REGEX = CHAR_MAX + 1,
1328 SNI_OPTION, 827 SNI_OPTION,
@@ -1333,81 +832,101 @@ bool process_arguments(int argc, char **argv) {
1333 AUTOMATIC_DECOMPRESSION, 832 AUTOMATIC_DECOMPRESSION,
1334 COOKIE_JAR, 833 COOKIE_JAR,
1335 HAPROXY_PROTOCOL, 834 HAPROXY_PROTOCOL,
1336 STATE_REGEX 835 STATE_REGEX,
836 OUTPUT_FORMAT
1337 }; 837 };
1338 838
1339 int option = 0; 839 static struct option longopts[] = {
1340 int got_plus = 0; 840 STD_LONG_OPTS,
1341 static struct option longopts[] = {STD_LONG_OPTS, 841 {"link", no_argument, 0, 'L'},
1342 {"link", no_argument, 0, 'L'}, 842 {"nohtml", no_argument, 0, 'n'},
1343 {"nohtml", no_argument, 0, 'n'}, 843 {"ssl", optional_argument, 0, 'S'},
1344 {"ssl", optional_argument, 0, 'S'}, 844 {"sni", no_argument, 0, SNI_OPTION},
1345 {"sni", no_argument, 0, SNI_OPTION}, 845 {"post", required_argument, 0, 'P'},
1346 {"post", required_argument, 0, 'P'}, 846 {"method", required_argument, 0, 'j'},
1347 {"method", required_argument, 0, 'j'}, 847 {"IP-address", required_argument, 0, 'I'},
1348 {"IP-address", required_argument, 0, 'I'}, 848 {"url", required_argument, 0, 'u'},
1349 {"url", required_argument, 0, 'u'}, 849 {"port", required_argument, 0, 'p'},
1350 {"port", required_argument, 0, 'p'}, 850 {"authorization", required_argument, 0, 'a'},
1351 {"authorization", required_argument, 0, 'a'}, 851 {"proxy-authorization", required_argument, 0, 'b'},
1352 {"proxy-authorization", required_argument, 0, 'b'}, 852 {"header-string", required_argument, 0, 'd'},
1353 {"header-string", required_argument, 0, 'd'}, 853 {"string", required_argument, 0, 's'},
1354 {"string", required_argument, 0, 's'}, 854 {"expect", required_argument, 0, 'e'},
1355 {"expect", required_argument, 0, 'e'}, 855 {"regex", required_argument, 0, 'r'},
1356 {"regex", required_argument, 0, 'r'}, 856 {"ereg", required_argument, 0, 'r'},
1357 {"ereg", required_argument, 0, 'r'}, 857 {"eregi", required_argument, 0, 'R'},
1358 {"eregi", required_argument, 0, 'R'}, 858 {"linespan", no_argument, 0, 'l'},
1359 {"linespan", no_argument, 0, 'l'}, 859 {"onredirect", required_argument, 0, 'f'},
1360 {"onredirect", required_argument, 0, 'f'}, 860 {"certificate", required_argument, 0, 'C'},
1361 {"certificate", required_argument, 0, 'C'}, 861 {"client-cert", required_argument, 0, 'J'},
1362 {"client-cert", required_argument, 0, 'J'}, 862 {"private-key", required_argument, 0, 'K'},
1363 {"private-key", required_argument, 0, 'K'}, 863 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1364 {"ca-cert", required_argument, 0, CA_CERT_OPTION}, 864 {"verify-cert", no_argument, 0, 'D'},
1365 {"verify-cert", no_argument, 0, 'D'}, 865 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
1366 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, 866 {"useragent", required_argument, 0, 'A'},
1367 {"useragent", required_argument, 0, 'A'}, 867 {"header", required_argument, 0, 'k'},
1368 {"header", required_argument, 0, 'k'}, 868 {"no-body", no_argument, 0, 'N'},
1369 {"no-body", no_argument, 0, 'N'}, 869 {"max-age", required_argument, 0, 'M'},
1370 {"max-age", required_argument, 0, 'M'}, 870 {"content-type", required_argument, 0, 'T'},
1371 {"content-type", required_argument, 0, 'T'}, 871 {"pagesize", required_argument, 0, 'm'},
1372 {"pagesize", required_argument, 0, 'm'}, 872 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1373 {"invert-regex", no_argument, NULL, INVERT_REGEX}, 873 {"state-regex", required_argument, 0, STATE_REGEX},
1374 {"state-regex", required_argument, 0, STATE_REGEX}, 874 {"use-ipv4", no_argument, 0, '4'},
1375 {"use-ipv4", no_argument, 0, '4'}, 875 {"use-ipv6", no_argument, 0, '6'},
1376 {"use-ipv6", no_argument, 0, '6'}, 876 {"extended-perfdata", no_argument, 0, 'E'},
1377 {"extended-perfdata", no_argument, 0, 'E'}, 877 {"show-body", no_argument, 0, 'B'},
1378 {"show-body", no_argument, 0, 'B'}, 878 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
1379 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, 879 {"http-version", required_argument, 0, HTTP_VERSION_OPTION},
1380 {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, 880 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION},
1381 {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, 881 {"cookie-jar", required_argument, 0, COOKIE_JAR},
1382 {"cookie-jar", required_argument, 0, COOKIE_JAR}, 882 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL},
1383 {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, 883 {"output-format", required_argument, 0, OUTPUT_FORMAT},
1384 {0, 0, 0, 0}}; 884 {0, 0, 0, 0}};
1385 885
1386 if (argc < 2) 886 check_curl_config_wrapper result = {
1387 return false; 887 .errorcode = OK,
888 .config = check_curl_config_init(),
889 };
1388 890
1389 /* support check_http compatible arguments */ 891 if (argc < 2) {
1390 for (c = 1; c < argc; c++) { 892 result.errorcode = ERROR;
1391 if (strcmp("-to", argv[c]) == 0) 893 return result;
1392 strcpy(argv[c], "-t");
1393 if (strcmp("-hn", argv[c]) == 0)
1394 strcpy(argv[c], "-H");
1395 if (strcmp("-wt", argv[c]) == 0)
1396 strcpy(argv[c], "-w");
1397 if (strcmp("-ct", argv[c]) == 0)
1398 strcpy(argv[c], "-c");
1399 if (strcmp("-nohtml", argv[c]) == 0)
1400 strcpy(argv[c], "-n");
1401 } 894 }
1402 895
1403 server_url = strdup(DEFAULT_SERVER_URL); 896 /* support check_http compatible arguments */
897 for (int index = 1; index < argc; index++) {
898 if (strcmp("-to", argv[index]) == 0) {
899 strcpy(argv[index], "-t");
900 }
901 if (strcmp("-hn", argv[index]) == 0) {
902 strcpy(argv[index], "-H");
903 }
904 if (strcmp("-wt", argv[index]) == 0) {
905 strcpy(argv[index], "-w");
906 }
907 if (strcmp("-ct", argv[index]) == 0) {
908 strcpy(argv[index], "-c");
909 }
910 if (strcmp("-nohtml", argv[index]) == 0) {
911 strcpy(argv[index], "-n");
912 }
913 }
1404 914
1405 while (1) { 915 int option = 0;
1406 c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); 916 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
1407 if (c == -1 || c == EOF || c == 1) 917 bool specify_port = false;
918 bool enable_tls = false;
919 char *tls_option_optarg = NULL;
920
921 while (true) {
922 int option_index = getopt_long(
923 argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB",
924 longopts, &option);
925 if (option_index == -1 || option_index == EOF || option_index == 1) {
1408 break; 926 break;
927 }
1409 928
1410 switch (c) { 929 switch (option_index) {
1411 case 'h': 930 case 'h':
1412 print_help(); 931 print_help();
1413 exit(STATE_UNKNOWN); 932 exit(STATE_UNKNOWN);
@@ -1421,270 +940,253 @@ bool process_arguments(int argc, char **argv) {
1421 verbose++; 940 verbose++;
1422 break; 941 break;
1423 case 't': /* timeout period */ 942 case 't': /* timeout period */
1424 if (!is_intnonneg(optarg)) 943 if (!is_intnonneg(optarg)) {
1425 usage2(_("Timeout interval must be a positive integer"), optarg); 944 usage2(_("Timeout interval must be a positive integer"), optarg);
1426 else 945 } else {
1427 socket_timeout = (int)strtol(optarg, NULL, 10); 946 result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10);
947 }
1428 break; 948 break;
1429 case 'c': /* critical time threshold */ 949 case 'c': /* critical time threshold */
1430 critical_thresholds = optarg; 950 {
1431 break; 951 mp_range_parsed critical_range = mp_parse_range_string(optarg);
952 if (critical_range.error != MP_PARSING_SUCCES) {
953 die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg);
954 }
955 result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range);
956 } break;
1432 case 'w': /* warning time threshold */ 957 case 'w': /* warning time threshold */
1433 warning_thresholds = optarg; 958 {
1434 break; 959 mp_range_parsed warning_range = mp_parse_range_string(optarg);
960
961 if (warning_range.error != MP_PARSING_SUCCES) {
962 die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg);
963 }
964 result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range);
965 } break;
1435 case 'H': /* virtual host */ 966 case 'H': /* virtual host */
1436 host_name = strdup(optarg); 967 result.config.initial_config.host_name = strdup(optarg);
1437 if (host_name[0] == '[') { 968 char *tmp_string;
1438 if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ 969 size_t host_name_length;
1439 virtual_port = atoi(p + 2); 970 if (result.config.initial_config.host_name[0] == '[') {
971 if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) !=
972 NULL) { /* [IPv6]:port */
973 result.config.initial_config.virtualPort = atoi(tmp_string + 2);
1440 /* cut off the port */ 974 /* cut off the port */
1441 host_name_length = strlen(host_name) - strlen(p) - 1; 975 host_name_length =
1442 free(host_name); 976 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1443 host_name = strndup(optarg, host_name_length); 977 free(result.config.initial_config.host_name);
978 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1444 } 979 }
1445 } else if ((p = strchr(host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ 980 } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL &&
1446 virtual_port = atoi(p); 981 strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */
982 result.config.initial_config.virtualPort = atoi(tmp_string);
1447 /* cut off the port */ 983 /* cut off the port */
1448 host_name_length = strlen(host_name) - strlen(p) - 1; 984 host_name_length =
1449 free(host_name); 985 strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1;
1450 host_name = strndup(optarg, host_name_length); 986 free(result.config.initial_config.host_name);
987 result.config.initial_config.host_name = strndup(optarg, host_name_length);
1451 } 988 }
1452 break; 989 break;
1453 case 'I': /* internet address */ 990 case 'I': /* internet address */
1454 server_address = strdup(optarg); 991 result.config.initial_config.server_address = strdup(optarg);
1455 break; 992 break;
1456 case 'u': /* URL path */ 993 case 'u': /* URL path */
1457 server_url = strdup(optarg); 994 result.config.initial_config.server_url = strdup(optarg);
1458 break; 995 break;
1459 case 'p': /* Server port */ 996 case 'p': /* Server port */
1460 if (!is_intnonneg(optarg)) 997 if (!is_intnonneg(optarg)) {
1461 usage2(_("Invalid port number, expecting a non-negative number"), optarg); 998 usage2(_("Invalid port number, expecting a non-negative number"), optarg);
1462 else { 999 } else {
1463 if (strtol(optarg, NULL, 10) > MAX_PORT) 1000 if (strtol(optarg, NULL, 10) > MAX_PORT) {
1464 usage2(_("Invalid port number, supplied port number is too big"), optarg); 1001 usage2(_("Invalid port number, supplied port number is too big"), optarg);
1465 server_port = (unsigned short)strtol(optarg, NULL, 10); 1002 }
1003 result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10);
1466 specify_port = true; 1004 specify_port = true;
1467 } 1005 }
1468 break; 1006 break;
1469 case 'a': /* authorization info */ 1007 case 'a': /* authorization info */
1470 strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); 1008 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1471 user_auth[MAX_INPUT_BUFFER - 1] = 0; 1009 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1472 break; 1010 break;
1473 case 'b': /* proxy-authorization info */ 1011 case 'b': /* proxy-authorization info */
1474 strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 1012 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1475 proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 1013 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1476 break; 1014 break;
1477 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ 1015 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1478 if (!http_post_data) 1016 if (!result.config.initial_config.http_post_data) {
1479 http_post_data = strdup(optarg); 1017 result.config.initial_config.http_post_data = strdup(optarg);
1480 if (!http_method) 1018 }
1481 http_method = strdup("POST"); 1019 if (!result.config.initial_config.http_method) {
1020 result.config.initial_config.http_method = strdup("POST");
1021 }
1482 break; 1022 break;
1483 case 'j': /* Set HTTP method */ 1023 case 'j': /* Set HTTP method */
1484 if (http_method) 1024 if (result.config.initial_config.http_method) {
1485 free(http_method); 1025 free(result.config.initial_config.http_method);
1486 http_method = strdup(optarg); 1026 }
1027 result.config.initial_config.http_method = strdup(optarg);
1487 break; 1028 break;
1488 case 'A': /* useragent */ 1029 case 'A': /* useragent */
1489 strncpy(user_agent, optarg, DEFAULT_BUFFER_SIZE); 1030 strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE);
1490 user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; 1031 result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0';
1491 break; 1032 break;
1492 case 'k': /* Additional headers */ 1033 case 'k': /* Additional headers */
1493 if (http_opt_headers_count == 0) 1034 if (result.config.curl_config.http_opt_headers_count == 0) {
1494 http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); 1035 result.config.curl_config.http_opt_headers =
1495 else 1036 malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1496 http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); 1037 } else {
1497 http_opt_headers[http_opt_headers_count - 1] = optarg; 1038 result.config.curl_config.http_opt_headers =
1039 realloc(result.config.curl_config.http_opt_headers,
1040 sizeof(char *) * (++result.config.curl_config.http_opt_headers_count));
1041 }
1042 result.config.curl_config
1043 .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg;
1498 break; 1044 break;
1499 case 'L': /* show html link */ 1045 case 'L': /* show html link */
1500 display_html = true;
1501 break;
1502 case 'n': /* do not show html link */ 1046 case 'n': /* do not show html link */
1503 display_html = false; 1047 // HTML link related options are deprecated
1504 break; 1048 break;
1505 case 'C': /* Check SSL cert validity */ 1049 case 'C': /* Check SSL cert validity */
1506#ifdef LIBCURL_FEATURE_SSL 1050#ifndef LIBCURL_FEATURE_SSL
1507 if ((temp = strchr(optarg, ',')) != NULL) { 1051 usage4(_("Invalid option - SSL is not available"));
1508 *temp = '\0';
1509 if (!is_intnonneg(optarg))
1510 usage2(_("Invalid certificate expiration period"), optarg);
1511 days_till_exp_warn = atoi(optarg);
1512 *temp = ',';
1513 temp++;
1514 if (!is_intnonneg(temp))
1515 usage2(_("Invalid certificate expiration period"), temp);
1516 days_till_exp_crit = atoi(temp);
1517 } else {
1518 days_till_exp_crit = 0;
1519 if (!is_intnonneg(optarg))
1520 usage2(_("Invalid certificate expiration period"), optarg);
1521 days_till_exp_warn = atoi(optarg);
1522 }
1523 check_cert = true;
1524 goto enable_ssl;
1525#endif 1052#endif
1053 {
1054 char *temp;
1055 if ((temp = strchr(optarg, ',')) != NULL) {
1056 *temp = '\0';
1057 if (!is_intnonneg(optarg)) {
1058 usage2(_("Invalid certificate expiration period"), optarg);
1059 }
1060 result.config.days_till_exp_warn = atoi(optarg);
1061 *temp = ',';
1062 temp++;
1063 if (!is_intnonneg(temp)) {
1064 usage2(_("Invalid certificate expiration period"), temp);
1065 }
1066 result.config.days_till_exp_crit = atoi(temp);
1067 } else {
1068 result.config.days_till_exp_crit = 0;
1069 if (!is_intnonneg(optarg)) {
1070 usage2(_("Invalid certificate expiration period"), optarg);
1071 }
1072 result.config.days_till_exp_warn = atoi(optarg);
1073 }
1074 result.config.check_cert = true;
1075 enable_tls = true;
1076 }
1077 break;
1526 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ 1078 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
1527#ifdef HAVE_SSL 1079#ifdef HAVE_SSL
1528 continue_after_check_cert = true; 1080 result.config.continue_after_check_cert = true;
1529 break; 1081 break;
1530#endif 1082#endif
1531 case 'J': /* use client certificate */ 1083 case 'J': /* use client certificate */
1532#ifdef LIBCURL_FEATURE_SSL 1084#ifndef LIBCURL_FEATURE_SSL
1533 test_file(optarg); 1085 usage4(_("Invalid option - SSL is not available"));
1534 client_cert = optarg;
1535 goto enable_ssl;
1536#endif 1086#endif
1537 case 'K': /* use client private key */
1538#ifdef LIBCURL_FEATURE_SSL
1539 test_file(optarg); 1087 test_file(optarg);
1540 client_privkey = optarg; 1088 result.config.curl_config.client_cert = optarg;
1541 goto enable_ssl; 1089 enable_tls = true;
1090 break;
1091 case 'K': /* use client private key */
1092#ifndef LIBCURL_FEATURE_SSL
1093 usage4(_("Invalid option - SSL is not available"));
1542#endif 1094#endif
1543#ifdef LIBCURL_FEATURE_SSL
1544 case CA_CERT_OPTION: /* use CA chain file */
1545 test_file(optarg); 1095 test_file(optarg);
1546 ca_cert = optarg; 1096 result.config.curl_config.client_privkey = optarg;
1547 goto enable_ssl; 1097 enable_tls = true;
1098 break;
1099 case CA_CERT_OPTION: /* use CA chain file */
1100#ifndef LIBCURL_FEATURE_SSL
1101 usage4(_("Invalid option - SSL is not available"));
1548#endif 1102#endif
1549#ifdef LIBCURL_FEATURE_SSL 1103 test_file(optarg);
1550 case 'D': /* verify peer certificate & host */ 1104 result.config.curl_config.ca_cert = optarg;
1551 verify_peer_and_host = true; 1105 enable_tls = true;
1552 break; 1106 break;
1107 case 'D': /* verify peer certificate & host */
1108#ifndef LIBCURL_FEATURE_SSL
1109 usage4(_("Invalid option - SSL is not available"));
1553#endif 1110#endif
1554 case 'S': /* use SSL */ 1111 result.config.curl_config.verify_peer_and_host = true;
1555#ifdef LIBCURL_FEATURE_SSL 1112 enable_tls = true;
1556 enable_ssl:
1557 use_ssl = true;
1558 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1559 * Only set if it's non-zero. This helps when we include multiple
1560 * parameters, like -S and -C combinations */
1561 ssl_version = CURL_SSLVERSION_DEFAULT;
1562 if (c == 'S' && optarg != NULL) {
1563 char *plus_ptr = strchr(optarg, '+');
1564 if (plus_ptr) {
1565 got_plus = 1;
1566 *plus_ptr = '\0';
1567 }
1568
1569 if (optarg[0] == '2')
1570 ssl_version = CURL_SSLVERSION_SSLv2;
1571 else if (optarg[0] == '3')
1572 ssl_version = CURL_SSLVERSION_SSLv3;
1573 else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0"))
1574# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1575 ssl_version = CURL_SSLVERSION_TLSv1_0;
1576# else
1577 ssl_version = CURL_SSLVERSION_DEFAULT;
1578# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1579 else if (!strcmp(optarg, "1.1"))
1580# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1581 ssl_version = CURL_SSLVERSION_TLSv1_1;
1582# else
1583 ssl_version = CURL_SSLVERSION_DEFAULT;
1584# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1585 else if (!strcmp(optarg, "1.2"))
1586# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1587 ssl_version = CURL_SSLVERSION_TLSv1_2;
1588# else
1589 ssl_version = CURL_SSLVERSION_DEFAULT;
1590# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1591 else if (!strcmp(optarg, "1.3"))
1592# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1593 ssl_version = CURL_SSLVERSION_TLSv1_3;
1594# else
1595 ssl_version = CURL_SSLVERSION_DEFAULT;
1596# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1597 else
1598 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
1599 }
1600# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1601 if (got_plus) {
1602 switch (ssl_version) {
1603 case CURL_SSLVERSION_TLSv1_3:
1604 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1605 break;
1606 case CURL_SSLVERSION_TLSv1_2:
1607 case CURL_SSLVERSION_TLSv1_1:
1608 case CURL_SSLVERSION_TLSv1_0:
1609 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1610 break;
1611 }
1612 } else {
1613 switch (ssl_version) {
1614 case CURL_SSLVERSION_TLSv1_3:
1615 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1616 break;
1617 case CURL_SSLVERSION_TLSv1_2:
1618 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1619 break;
1620 case CURL_SSLVERSION_TLSv1_1:
1621 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1622 break;
1623 case CURL_SSLVERSION_TLSv1_0:
1624 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1625 break;
1626 }
1627 }
1628# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1629 if (verbose >= 2)
1630 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1631 if (!specify_port)
1632 server_port = HTTPS_PORT;
1633 break; 1113 break;
1634#else /* LIBCURL_FEATURE_SSL */ 1114 case 'S': /* use SSL */
1635 /* -C -J and -K fall through to here without SSL */ 1115 tls_option_optarg = optarg;
1116 enable_tls = true;
1117#ifndef LIBCURL_FEATURE_SSL
1636 usage4(_("Invalid option - SSL is not available")); 1118 usage4(_("Invalid option - SSL is not available"));
1119#endif
1637 break; 1120 break;
1638 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ 1121 case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */
1639 use_sni = true; 1122#ifndef LIBCURL_FEATURE_SSL
1640 break; 1123 usage4(_("Invalid option - SSL is not available"));
1641#endif /* LIBCURL_FEATURE_SSL */ 1124#endif /* LIBCURL_FEATURE_SSL */
1125 break;
1642 case MAX_REDIRS_OPTION: 1126 case MAX_REDIRS_OPTION:
1643 if (!is_intnonneg(optarg)) 1127 if (!is_intnonneg(optarg)) {
1644 usage2(_("Invalid max_redirs count"), optarg); 1128 usage2(_("Invalid max_redirs count"), optarg);
1645 else { 1129 } else {
1646 max_depth = atoi(optarg); 1130 result.config.max_depth = atoi(optarg);
1647 } 1131 }
1648 break; 1132 break;
1649 case 'f': /* onredirect */ 1133 case 'f': /* onredirect */
1650 if (!strcmp(optarg, "ok")) 1134 if (!strcmp(optarg, "ok")) {
1651 onredirect = STATE_OK; 1135 result.config.on_redirect_result_state = STATE_OK;
1652 else if (!strcmp(optarg, "warning")) 1136 result.config.on_redirect_dependent = false;
1653 onredirect = STATE_WARNING; 1137 } else if (!strcmp(optarg, "warning")) {
1654 else if (!strcmp(optarg, "critical")) 1138 result.config.on_redirect_result_state = STATE_WARNING;
1655 onredirect = STATE_CRITICAL; 1139 result.config.on_redirect_dependent = false;
1656 else if (!strcmp(optarg, "unknown")) 1140 } else if (!strcmp(optarg, "critical")) {
1657 onredirect = STATE_UNKNOWN; 1141 result.config.on_redirect_result_state = STATE_CRITICAL;
1658 else if (!strcmp(optarg, "follow")) 1142 result.config.on_redirect_dependent = false;
1659 onredirect = STATE_DEPENDENT; 1143 } else if (!strcmp(optarg, "unknown")) {
1660 else if (!strcmp(optarg, "stickyport")) 1144 result.config.on_redirect_result_state = STATE_UNKNOWN;
1661 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; 1145 result.config.on_redirect_dependent = false;
1662 else if (!strcmp(optarg, "sticky")) 1146 } else if (!strcmp(optarg, "follow")) {
1663 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; 1147 result.config.on_redirect_dependent = true;
1664 else if (!strcmp(optarg, "follow")) 1148 } else if (!strcmp(optarg, "stickyport")) {
1665 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; 1149 result.config.on_redirect_dependent = true;
1666 else if (!strcmp(optarg, "curl")) 1150 result.config.followmethod = FOLLOW_HTTP_CURL,
1667 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; 1151 result.config.followsticky = STICKY_HOST | STICKY_PORT;
1668 else 1152 } else if (!strcmp(optarg, "sticky")) {
1153 result.config.on_redirect_dependent = true;
1154 result.config.followmethod = FOLLOW_HTTP_CURL,
1155 result.config.followsticky = STICKY_HOST;
1156 } else if (!strcmp(optarg, "follow")) {
1157 result.config.on_redirect_dependent = true;
1158 result.config.followmethod = FOLLOW_HTTP_CURL,
1159 result.config.followsticky = STICKY_NONE;
1160 } else if (!strcmp(optarg, "curl")) {
1161 result.config.on_redirect_dependent = true;
1162 result.config.followmethod = FOLLOW_LIBCURL;
1163 } else {
1669 usage2(_("Invalid onredirect option"), optarg); 1164 usage2(_("Invalid onredirect option"), optarg);
1670 if (verbose >= 2) 1165 }
1671 printf(_("* Following redirects set to %s\n"), state_text(onredirect)); 1166 if (verbose >= 2) {
1167 if (result.config.on_redirect_dependent) {
1168 printf(_("* Following redirects\n"));
1169 } else {
1170 printf(_("* Following redirects set to state %s\n"),
1171 state_text(result.config.on_redirect_result_state));
1172 }
1173 }
1672 break; 1174 break;
1673 case 'd': /* string or substring */ 1175 case 'd': /* string or substring */
1674 strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); 1176 strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1);
1675 header_expect[MAX_INPUT_BUFFER - 1] = 0; 1177 result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0;
1676 break; 1178 break;
1677 case 's': /* string or substring */ 1179 case 's': /* string or substring */
1678 strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); 1180 strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1);
1679 string_expect[MAX_INPUT_BUFFER - 1] = 0; 1181 result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0;
1680 break; 1182 break;
1681 case 'e': /* string or substring */ 1183 case 'e': /* string or substring */
1682 strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); 1184 strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1);
1683 server_expect[MAX_INPUT_BUFFER - 1] = 0; 1185 result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0;
1684 server_expect_yn = 1; 1186 result.config.server_expect.is_present = true;
1685 break; 1187 break;
1686 case 'T': /* Content-type */ 1188 case 'T': /* Content-type */
1687 http_content_type = strdup(optarg); 1189 result.config.curl_config.http_content_type = strdup(optarg);
1688 break; 1190 break;
1689 case 'l': /* linespan */ 1191 case 'l': /* linespan */
1690 cflags &= ~REG_NEWLINE; 1192 cflags &= ~REG_NEWLINE;
@@ -1693,185 +1195,258 @@ bool process_arguments(int argc, char **argv) {
1693 cflags |= REG_ICASE; 1195 cflags |= REG_ICASE;
1694 // fall through 1196 // fall through
1695 case 'r': /* regex */ 1197 case 'r': /* regex */
1696 strncpy(regexp, optarg, MAX_RE_SIZE - 1); 1198 strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1);
1697 regexp[MAX_RE_SIZE - 1] = 0; 1199 result.config.regexp[MAX_RE_SIZE - 1] = 0;
1698 errcode = regcomp(&preg, regexp, cflags); 1200 regex_t preg;
1201 int errcode = regcomp(&preg, result.config.regexp, cflags);
1699 if (errcode != 0) { 1202 if (errcode != 0) {
1700 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 1203 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1701 printf(_("Could Not Compile Regular Expression: %s"), errbuf); 1204 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
1702 return false; 1205 result.errorcode = ERROR;
1206 return result;
1703 } 1207 }
1208
1209 result.config.compiled_regex = preg;
1704 break; 1210 break;
1705 case INVERT_REGEX: 1211 case INVERT_REGEX:
1706 invert_regex = true; 1212 result.config.invert_regex = true;
1707 break; 1213 break;
1708 case STATE_REGEX: 1214 case STATE_REGEX:
1709 if (!strcasecmp(optarg, "critical")) 1215 if (!strcasecmp(optarg, "critical")) {
1710 state_regex = STATE_CRITICAL; 1216 result.config.state_regex = STATE_CRITICAL;
1711 else if (!strcasecmp(optarg, "warning")) 1217 } else if (!strcasecmp(optarg, "warning")) {
1712 state_regex = STATE_WARNING; 1218 result.config.state_regex = STATE_WARNING;
1713 else 1219 } else {
1714 usage2(_("Invalid state-regex option"), optarg); 1220 usage2(_("Invalid state-regex option"), optarg);
1221 }
1715 break; 1222 break;
1716 case '4': 1223 case '4':
1717 address_family = AF_INET; 1224 result.config.curl_config.sin_family = AF_INET;
1718 break; 1225 break;
1719 case '6': 1226 case '6':
1720#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) 1227#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
1721 address_family = AF_INET6; 1228 result.config.curl_config.sin_family = AF_INET6;
1722#else 1229#else
1723 usage4(_("IPv6 support not available")); 1230 usage4(_("IPv6 support not available"));
1724#endif 1231#endif
1725 break; 1232 break;
1726 case 'm': /* min_page_length */ 1233 case 'm': /* min_page_length */
1727 { 1234 {
1728 char *tmp; 1235 mp_range_parsed foo = mp_parse_range_string(optarg);
1729 if (strchr(optarg, ':') != (char *)NULL) { 1236
1730 /* range, so get two values, min:max */ 1237 if (foo.error != MP_PARSING_SUCCES) {
1731 tmp = strtok(optarg, ":"); 1238 die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg);
1732 if (tmp == NULL) { 1239 }
1733 printf("Bad format: try \"-m min:max\"\n"); 1240
1734 exit(STATE_WARNING); 1241 result.config.page_length_limits = foo.range;
1735 } else 1242 result.config.page_length_limits_is_set = true;
1736 min_page_len = atoi(tmp);
1737
1738 tmp = strtok(NULL, ":");
1739 if (tmp == NULL) {
1740 printf("Bad format: try \"-m min:max\"\n");
1741 exit(STATE_WARNING);
1742 } else
1743 max_page_len = atoi(tmp);
1744 } else
1745 min_page_len = atoi(optarg);
1746 break; 1243 break;
1747 } 1244 }
1748 case 'N': /* no-body */ 1245 case 'N': /* no-body */
1749 no_body = true; 1246 result.config.initial_config.no_body = true;
1750 break; 1247 break;
1751 case 'M': /* max-age */ 1248 case 'M': /* max-age */
1752 { 1249 {
1753 int L = strlen(optarg); 1250 size_t option_length = strlen(optarg);
1754 if (L && optarg[L - 1] == 'm') 1251 if (option_length && optarg[option_length - 1] == 'm') {
1755 maximum_age = atoi(optarg) * 60; 1252 result.config.maximum_age = atoi(optarg) * 60;
1756 else if (L && optarg[L - 1] == 'h') 1253 } else if (option_length && optarg[option_length - 1] == 'h') {
1757 maximum_age = atoi(optarg) * 60 * 60; 1254 result.config.maximum_age = atoi(optarg) * 60 * 60;
1758 else if (L && optarg[L - 1] == 'd') 1255 } else if (option_length && optarg[option_length - 1] == 'd') {
1759 maximum_age = atoi(optarg) * 60 * 60 * 24; 1256 result.config.maximum_age = atoi(optarg) * 60 * 60 * 24;
1760 else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) 1257 } else if (option_length &&
1761 maximum_age = atoi(optarg); 1258 (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) {
1762 else { 1259 result.config.maximum_age = atoi(optarg);
1260 } else {
1763 fprintf(stderr, "unparsable max-age: %s\n", optarg); 1261 fprintf(stderr, "unparsable max-age: %s\n", optarg);
1764 exit(STATE_WARNING); 1262 exit(STATE_WARNING);
1765 } 1263 }
1766 if (verbose >= 2) 1264 if (verbose >= 2) {
1767 printf("* Maximal age of document set to %d seconds\n", maximum_age); 1265 printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age);
1266 }
1768 } break; 1267 } break;
1769 case 'E': /* show extended perfdata */ 1268 case 'E': /* show extended perfdata */
1770 show_extended_perfdata = true; 1269 result.config.show_extended_perfdata = true;
1771 break; 1270 break;
1772 case 'B': /* print body content after status line */ 1271 case 'B': /* print body content after status line */
1773 show_body = true; 1272 result.config.show_body = true;
1774 break; 1273 break;
1775 case HTTP_VERSION_OPTION: 1274 case HTTP_VERSION_OPTION:
1776 curl_http_version = CURL_HTTP_VERSION_NONE; 1275 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1777 if (strcmp(optarg, "1.0") == 0) { 1276 if (strcmp(optarg, "1.0") == 0) {
1778 curl_http_version = CURL_HTTP_VERSION_1_0; 1277 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0;
1779 } else if (strcmp(optarg, "1.1") == 0) { 1278 } else if (strcmp(optarg, "1.1") == 0) {
1780 curl_http_version = CURL_HTTP_VERSION_1_1; 1279 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1;
1781 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { 1280 } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) {
1782#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) 1281#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0)
1783 curl_http_version = CURL_HTTP_VERSION_2_0; 1282 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0;
1784#else 1283#else
1785 curl_http_version = CURL_HTTP_VERSION_NONE; 1284 result.config.curl_http_version = CURL_HTTP_VERSION_NONE;
1786#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ 1285#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */
1286 } else if ((strcmp(optarg, "3") == 0)) {
1287#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0)
1288 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3;
1289#else
1290 result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE;
1291#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */
1787 } else { 1292 } else {
1788 fprintf(stderr, "unknown http-version parameter: %s\n", optarg); 1293 fprintf(stderr, "unknown http-version parameter: %s\n", optarg);
1789 exit(STATE_WARNING); 1294 exit(STATE_WARNING);
1790 } 1295 }
1791 break; 1296 break;
1792 case AUTOMATIC_DECOMPRESSION: 1297 case AUTOMATIC_DECOMPRESSION:
1793 automatic_decompression = true; 1298 result.config.curl_config.automatic_decompression = true;
1794 break; 1299 break;
1795 case COOKIE_JAR: 1300 case COOKIE_JAR:
1796 cookie_jar_file = optarg; 1301 result.config.curl_config.cookie_jar_file = optarg;
1797 break; 1302 break;
1798 case HAPROXY_PROTOCOL: 1303 case HAPROXY_PROTOCOL:
1799 haproxy_protocol = true; 1304 result.config.curl_config.haproxy_protocol = true;
1800 break; 1305 break;
1801 case '?': 1306 case '?':
1802 /* print short usage statement if args not parsable */ 1307 /* print short usage statement if args not parsable */
1803 usage5(); 1308 usage5();
1804 break; 1309 break;
1310 case OUTPUT_FORMAT: {
1311 parsed_output_format parser = mp_parse_output_format(optarg);
1312 if (!parser.parsing_success) {
1313 // TODO List all available formats here, maybe add anothoer usage function
1314 printf("Invalid output format: %s\n", optarg);
1315 exit(STATE_UNKNOWN);
1316 }
1317
1318 result.config.output_format_is_set = true;
1319 result.config.output_format = parser.output_format;
1320 break;
1321 }
1805 } 1322 }
1806 } 1323 }
1807 1324
1808 c = optind; 1325 if (enable_tls) {
1326 bool got_plus = false;
1327 result.config.initial_config.use_ssl = true;
1328 /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default.
1329 * Only set if it's non-zero. This helps when we include multiple
1330 * parameters, like -S and -C combinations */
1331 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT;
1332 if (tls_option_optarg != NULL) {
1333 char *plus_ptr = strchr(optarg, '+');
1334 if (plus_ptr) {
1335 got_plus = true;
1336 *plus_ptr = '\0';
1337 }
1809 1338
1810 if (server_address == NULL && c < argc) 1339 if (optarg[0] == '2') {
1811 server_address = strdup(argv[c++]); 1340 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2;
1341 } else if (optarg[0] == '3') {
1342 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3;
1343 } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) {
1344#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1345 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0;
1346#else
1347 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1348#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1349 } else if (!strcmp(optarg, "1.1")) {
1350#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1351 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1;
1352#else
1353 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1354#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1355 } else if (!strcmp(optarg, "1.2")) {
1356#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1357 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2;
1358#else
1359 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1360#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1361 } else if (!strcmp(optarg, "1.3")) {
1362#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1363 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3;
1364#else
1365 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1366#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1367 } else {
1368 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 "
1369 "(with optional '+' suffix)"));
1370 }
1371 }
1372#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1373 if (got_plus) {
1374 switch (result.config.curl_config.ssl_version) {
1375 case CURL_SSLVERSION_TLSv1_3:
1376 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1377 break;
1378 case CURL_SSLVERSION_TLSv1_2:
1379 case CURL_SSLVERSION_TLSv1_1:
1380 case CURL_SSLVERSION_TLSv1_0:
1381 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1382 break;
1383 }
1384 } else {
1385 switch (result.config.curl_config.ssl_version) {
1386 case CURL_SSLVERSION_TLSv1_3:
1387 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1388 break;
1389 case CURL_SSLVERSION_TLSv1_2:
1390 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1391 break;
1392 case CURL_SSLVERSION_TLSv1_1:
1393 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1394 break;
1395 case CURL_SSLVERSION_TLSv1_0:
1396 result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1397 break;
1398 }
1399 }
1400#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1401 if (verbose >= 2) {
1402 printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version);
1403 }
1404 if (!specify_port) {
1405 result.config.initial_config.serverPort = HTTPS_PORT;
1406 }
1407 }
1812 1408
1813 if (host_name == NULL && c < argc) 1409 int option_counter = optind;
1814 host_name = strdup(argv[c++]);
1815 1410
1816 if (server_address == NULL) { 1411 if (result.config.initial_config.server_address == NULL && option_counter < argc) {
1817 if (host_name == NULL) 1412 result.config.initial_config.server_address = strdup(argv[option_counter++]);
1818 usage4(_("You must specify a server address or host name"));
1819 else
1820 server_address = strdup(host_name);
1821 } 1413 }
1822 1414
1823 set_thresholds(&thlds, warning_thresholds, critical_thresholds); 1415 if (result.config.initial_config.host_name == NULL && option_counter < argc) {
1416 result.config.initial_config.host_name = strdup(argv[option_counter++]);
1417 }
1824 1418
1825 if (critical_thresholds && thlds->critical->end > (double)socket_timeout) 1419 if (result.config.initial_config.server_address == NULL) {
1826 socket_timeout = (int)thlds->critical->end + 1; 1420 if (result.config.initial_config.host_name == NULL) {
1827 if (verbose >= 2) 1421 usage4(_("You must specify a server address or host name"));
1828 printf("* Socket timeout set to %ld seconds\n", socket_timeout); 1422 } else {
1423 result.config.initial_config.server_address =
1424 strdup(result.config.initial_config.host_name);
1425 }
1426 }
1829 1427
1830 if (http_method == NULL) 1428 if (result.config.initial_config.http_method == NULL) {
1831 http_method = strdup("GET"); 1429 result.config.initial_config.http_method = strdup("GET");
1430 }
1832 1431
1833 if (client_cert && !client_privkey) 1432 if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) {
1834 usage4(_("If you use a client certificate you must also specify a private key file")); 1433 usage4(_("If you use a client certificate you must also specify a private key file"));
1835
1836 if (virtual_port == 0)
1837 virtual_port = server_port;
1838 else {
1839 if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT))
1840 if (!specify_port)
1841 server_port = virtual_port;
1842 } 1434 }
1843 1435
1844 return true; 1436 if (result.config.initial_config.virtualPort == 0) {
1845} 1437 result.config.initial_config.virtualPort = result.config.initial_config.serverPort;
1846 1438 } else {
1847char *perfd_time(double elapsed_time) { 1439 if ((result.config.initial_config.use_ssl &&
1848 return fperfdata("time", elapsed_time, "s", thlds->warning ? true : false, thlds->warning ? thlds->warning->end : 0, 1440 result.config.initial_config.serverPort == HTTPS_PORT) ||
1849 thlds->critical ? true : false, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); 1441 (!result.config.initial_config.use_ssl &&
1850} 1442 result.config.initial_config.serverPort == HTTP_PORT)) {
1851 1443 if (!specify_port) {
1852char *perfd_time_connect(double elapsed_time_connect) { 1444 result.config.initial_config.serverPort = result.config.initial_config.virtualPort;
1853 return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1445 }
1854} 1446 }
1855 1447 }
1856char *perfd_time_ssl(double elapsed_time_ssl) {
1857 return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1858}
1859
1860char *perfd_time_headers(double elapsed_time_headers) {
1861 return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1862}
1863
1864char *perfd_time_firstbyte(double elapsed_time_firstbyte) {
1865 return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1866}
1867
1868char *perfd_time_transfer(double elapsed_time_transfer) {
1869 return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1870}
1871 1448
1872char *perfd_size(int page_len) { 1449 return result;
1873 return perfdata("size", page_len, "B", (min_page_len > 0 ? true : false), min_page_len, (min_page_len > 0 ? true : false), 0, true, 0,
1874 false, 0);
1875} 1450}
1876 1451
1877void print_help(void) { 1452void print_help(void) {
@@ -1885,7 +1460,8 @@ void print_help(void) {
1885 printf("%s\n", _("strings and regular expressions, check connection times, and report on")); 1460 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1886 printf("%s\n", _("certificate expiration times.")); 1461 printf("%s\n", _("certificate expiration times."));
1887 printf("\n"); 1462 printf("\n");
1888 printf("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); 1463 printf("%s\n",
1464 _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1889 printf("%s\n", _("as possible.")); 1465 printf("%s\n", _("as possible."));
1890 1466
1891 printf("\n\n"); 1467 printf("\n\n");
@@ -1903,7 +1479,8 @@ void print_help(void) {
1903 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); 1479 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1904 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); 1480 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1905 printf(" %s\n", "-I, --IP-address=ADDRESS"); 1481 printf(" %s\n", "-I, --IP-address=ADDRESS");
1906 printf(" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); 1482 printf(" %s\n",
1483 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1907 printf(" %s\n", "-p, --port=INTEGER"); 1484 printf(" %s\n", "-p, --port=INTEGER");
1908 printf(" %s", _("Port number (default: ")); 1485 printf(" %s", _("Port number (default: "));
1909 printf("%d)\n", HTTP_PORT); 1486 printf("%d)\n", HTTP_PORT);
@@ -1912,27 +1489,36 @@ void print_help(void) {
1912 1489
1913#ifdef LIBCURL_FEATURE_SSL 1490#ifdef LIBCURL_FEATURE_SSL
1914 printf(" %s\n", "-S, --ssl=VERSION[+]"); 1491 printf(" %s\n", "-S, --ssl=VERSION[+]");
1915 printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 1492 printf(" %s\n",
1493 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1916 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 1494 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1917 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); 1495 printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are "
1918 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually disabled in libcurl")); 1496 "also accepted."));
1497 printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually "
1498 "disabled in libcurl"));
1919 printf(" %s\n", "--sni"); 1499 printf(" %s\n", "--sni");
1920 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1500 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1921# if LIBCURL_VERSION_NUM >= 0x071801 1501# if LIBCURL_VERSION_NUM >= 0x071801
1922 printf(" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); 1502 printf(" %s\n",
1503 _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1923 printf(" %s\n", _(" SNI only really works since TLSv1.0")); 1504 printf(" %s\n", _(" SNI only really works since TLSv1.0"));
1924# else 1505# else
1925 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); 1506 printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1926# endif 1507# endif
1927 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1508 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1928 printf(" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); 1509 printf(" %s\n",
1929 printf(" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the")); 1510 _("Minimum number of days a certificate has to be valid. Port defaults to 443."));
1930 printf(" %s\n", _("first agument's value. If there is a second argument and the certificate's")); 1511 printf(" %s\n",
1512 _("A STATE_WARNING is returned if the certificate has a validity less than the"));
1513 printf(" %s\n",
1514 _("first agument's value. If there is a second argument and the certificate's"));
1931 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); 1515 printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned."));
1932 printf(" %s\n", _("(When this option is used the URL is not checked by default. You can use")); 1516 printf(" %s\n",
1517 _("(When this option is used the URL is not checked by default. You can use"));
1933 printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); 1518 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1934 printf(" %s\n", "--continue-after-certificate"); 1519 printf(" %s\n", "--continue-after-certificate");
1935 printf(" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); 1520 printf(" %s\n",
1521 _("Allows the HTTP check to continue after performing the certificate check."));
1936 printf(" %s\n", _("Does nothing unless -C is used.")); 1522 printf(" %s\n", _("Does nothing unless -C is used."));
1937 printf(" %s\n", "-J, --client-cert=FILE"); 1523 printf(" %s\n", "-J, --client-cert=FILE");
1938 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1524 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
@@ -1950,7 +1536,8 @@ void print_help(void) {
1950 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 1536 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1951 printf(" %s", _("the first (status) line of the server response (default: ")); 1537 printf(" %s", _("the first (status) line of the server response (default: "));
1952 printf("%s)\n", HTTP_EXPECT); 1538 printf("%s)\n", HTTP_EXPECT);
1953 printf(" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); 1539 printf(" %s\n",
1540 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1954 printf(" %s\n", "-d, --header-string=STRING"); 1541 printf(" %s\n", "-d, --header-string=STRING");
1955 printf(" %s\n", _("String to expect in the response headers")); 1542 printf(" %s\n", _("String to expect in the response headers"));
1956 printf(" %s\n", "-s, --string=STRING"); 1543 printf(" %s\n", "-s, --string=STRING");
@@ -1959,7 +1546,8 @@ void print_help(void) {
1959 printf(" %s\n", _("URL to GET or POST (default: /)")); 1546 printf(" %s\n", _("URL to GET or POST (default: /)"));
1960 printf(" %s\n", "-P, --post=STRING"); 1547 printf(" %s\n", "-P, --post=STRING");
1961 printf(" %s\n", _("URL decoded http POST data")); 1548 printf(" %s\n", _("URL decoded http POST data"));
1962 printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); 1549 printf(" %s\n",
1550 "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1963 printf(" %s\n", _("Set HTTP method.")); 1551 printf(" %s\n", _("Set HTTP method."));
1964 printf(" %s\n", "-N, --no-body"); 1552 printf(" %s\n", "-N, --no-body");
1965 printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); 1553 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
@@ -1979,7 +1567,8 @@ void print_help(void) {
1979 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); 1567 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1980 printf(" %s\n", _("can be changed with --state--regex)")); 1568 printf(" %s\n", _("can be changed with --state--regex)"));
1981 printf(" %s\n", "--state-regex=STATE"); 1569 printf(" %s\n", "--state-regex=STATE");
1982 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of \"critical\",\"warning\"")); 1570 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1571 "\"critical\",\"warning\""));
1983 printf(" %s\n", "-a, --authorization=AUTH_PAIR"); 1572 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1984 printf(" %s\n", _("Username:password on sites with basic authentication")); 1573 printf(" %s\n", _("Username:password on sites with basic authentication"));
1985 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1574 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
@@ -1987,13 +1576,14 @@ void print_help(void) {
1987 printf(" %s\n", "-A, --useragent=STRING"); 1576 printf(" %s\n", "-A, --useragent=STRING");
1988 printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); 1577 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1989 printf(" %s\n", "-k, --header=STRING"); 1578 printf(" %s\n", "-k, --header=STRING");
1990 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); 1579 printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for "
1580 "additional headers"));
1991 printf(" %s\n", "-E, --extended-perfdata"); 1581 printf(" %s\n", "-E, --extended-perfdata");
1992 printf(" %s\n", _("Print additional performance data")); 1582 printf(" %s\n", _("Print additional performance data"));
1993 printf(" %s\n", "-B, --show-body"); 1583 printf(" %s\n", "-B, --show-body");
1994 printf(" %s\n", _("Print body content below status line")); 1584 printf(" %s\n", _("Print body content below status line"));
1995 printf(" %s\n", "-L, --link"); 1585 // printf(" %s\n", "-L, --link");
1996 printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1586 // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1997 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>"); 1587 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1998 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1588 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1999 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1589 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
@@ -2003,20 +1593,25 @@ void print_help(void) {
2003 printf(" %s", _("Maximal number of redirects (default: ")); 1593 printf(" %s", _("Maximal number of redirects (default: "));
2004 printf("%d)\n", DEFAULT_MAX_REDIRS); 1594 printf("%d)\n", DEFAULT_MAX_REDIRS);
2005 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1595 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
2006 printf(" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1596 printf(" %s\n",
1597 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
2007 printf("\n"); 1598 printf("\n");
2008 printf(" %s\n", "--http-version=VERSION"); 1599 printf(" %s\n", "--http-version=VERSION");
2009 printf(" %s\n", _("Connect via specific HTTP protocol.")); 1600 printf(" %s\n", _("Connect via specific HTTP protocol."));
2010 printf(" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); 1601 printf(" %s\n",
1602 _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)"));
2011 printf(" %s\n", "--enable-automatic-decompression"); 1603 printf(" %s\n", "--enable-automatic-decompression");
2012 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); 1604 printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING)."));
2013 printf(" %s\n", "--haproxy-protocol"); 1605 printf(" %s\n", "--haproxy-protocol");
2014 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); 1606 printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL)."));
2015 printf(" %s\n", "--cookie-jar=FILE"); 1607 printf(" %s\n", "--cookie-jar=FILE");
2016 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); 1608 printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested."));
2017 printf(" %s\n", _("Specify an empty string as FILE to enable curl's cookie engine without saving")); 1609 printf(" %s\n",
2018 printf(" %s\n", _("the cookies to disk. Only enabling the engine without saving to disk requires")); 1610 _("Specify an empty string as FILE to enable curl's cookie engine without saving"));
2019 printf(" %s\n", _("handling multiple requests internally to curl, so use it with --onredirect=curl")); 1611 printf(" %s\n",
1612 _("the cookies to disk. Only enabling the engine without saving to disk requires"));
1613 printf(" %s\n",
1614 _("handling multiple requests internally to curl, so use it with --onredirect=curl"));
2020 printf("\n"); 1615 printf("\n");
2021 1616
2022 printf(UT_WARN_CRIT); 1617 printf(UT_WARN_CRIT);
@@ -2025,13 +1620,18 @@ void print_help(void) {
2025 1620
2026 printf(UT_VERBOSE); 1621 printf(UT_VERBOSE);
2027 1622
1623 printf(UT_OUTPUT_FORMAT);
1624
2028 printf("\n"); 1625 printf("\n");
2029 printf("%s\n", _("Notes:")); 1626 printf("%s\n", _("Notes:"));
2030 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); 1627 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
2031 printf(" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); 1628 printf(" %s\n",
2032 printf(" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); 1629 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1630 printf(" %s\n",
1631 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
2033 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); 1632 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
2034 printf(" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); 1633 printf(" %s\n",
1634 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
2035 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); 1635 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
2036 1636
2037#ifdef LIBCURL_FEATURE_SSL 1637#ifdef LIBCURL_FEATURE_SSL
@@ -2047,38 +1647,53 @@ void print_help(void) {
2047 printf("%s\n", _("Examples:")); 1647 printf("%s\n", _("Examples:"));
2048 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); 1648 printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
2049 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); 1649 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
2050 printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1650 printf(" %s\n",
2051 printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1651 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1652 printf(" %s\n",
1653 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2052 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1654 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2053 printf("\n"); 1655 printf("\n");
2054 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); 1656 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
2055 printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); 1657 printf(" %s\n",
2056 printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1658 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
2057 printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); 1659 printf(" %s\n",
1660 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1661 printf(" %s\n",
1662 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
2058 printf(" %s\n\n", _("the certificate is expired.")); 1663 printf(" %s\n\n", _("the certificate is expired."));
2059 printf("\n"); 1664 printf("\n");
2060 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); 1665 printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
2061 printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); 1666 printf(" %s\n",
2062 printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1667 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1668 printf(" %s\n",
1669 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
2063 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); 1670 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
2064 printf(" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); 1671 printf(" %s\n",
1672 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
2065#endif 1673#endif
2066 1674
2067 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 1675 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
2068 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1676 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2069 printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 1677 printf(" %s\n",
1678 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
2070 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1679 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2071 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); 1680 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
1681 "-H www.monitoring-plugins.org"));
2072 1682
2073#ifdef LIBCURL_FEATURE_SSL 1683#ifdef LIBCURL_FEATURE_SSL
2074 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); 1684 printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
2075 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1685 printf(" %s\n", _("It is recommended to use an environment proxy like:"));
2076 printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); 1686 printf(" %s\n",
1687 _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
2077 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1688 printf(" %s\n", _("legacy proxy requests in check_http style still work:"));
2078 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); 1689 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j "
2079 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>")); 1690 "CONNECT -H www.verisign.com "));
2080 printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1691 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
2081 printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1692 "-S(sl) -j CONNECT -H <webserver>"));
1693 printf(" %s\n",
1694 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1695 printf(" %s\n",
1696 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
2082 printf(" %s\n", _("a STATE_CRITICAL will be returned.")); 1697 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
2083 1698
2084#endif 1699#endif
@@ -2089,10 +1704,12 @@ void print_help(void) {
2089void print_usage(void) { 1704void print_usage(void) {
2090 printf("%s\n", _("Usage:")); 1705 printf("%s\n", _("Usage:"));
2091 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); 1706 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
2092 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>] [-D]\n"); 1707 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
1708 "file>] [-D]\n");
2093 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1709 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
2094 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); 1710 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
2095 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1711 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1712 "regex>]\n");
2096 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1713 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
2097 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); 1714 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
2098 printf(" [-T <content-type>] [-j method]\n"); 1715 printf(" [-T <content-type>] [-j method]\n");
@@ -2109,435 +1726,49 @@ void print_usage(void) {
2109 1726
2110void print_curl_version(void) { printf("%s\n", curl_version()); } 1727void print_curl_version(void) { printf("%s\n", curl_version()); }
2111 1728
2112int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) {
2113 buf->bufsize = DEFAULT_BUFFER_SIZE;
2114 buf->buflen = 0;
2115 buf->buf = (char *)malloc((size_t)buf->bufsize);
2116 if (buf->buf == NULL)
2117 return -1;
2118 return 0;
2119}
2120
2121size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
2122 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
2123
2124 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
2125 buf->bufsize = buf->bufsize * 2;
2126 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
2127 if (buf->buf == NULL) {
2128 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
2129 return -1;
2130 }
2131 }
2132
2133 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
2134 buf->buflen += size * nmemb;
2135 buf->buf[buf->buflen] = '\0';
2136
2137 return (int)(size * nmemb);
2138}
2139
2140size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
2141 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
2142
2143 size_t n = min(nmemb * size, buf->buflen - buf->pos);
2144
2145 memcpy(buffer, buf->buf + buf->pos, n);
2146 buf->pos += n;
2147
2148 return (int)n;
2149}
2150
2151void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
2152 free(buf->buf);
2153 buf->buf = NULL;
2154}
2155
2156int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) {
2157 buf->buflen = datalen;
2158 buf->buf = (char *)malloc((size_t)buf->buflen);
2159 if (buf->buf == NULL)
2160 return -1;
2161 memcpy(buf->buf, data, datalen);
2162 buf->pos = 0;
2163 return 0;
2164}
2165
2166void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
2167 free(buf->buf);
2168 buf->buf = NULL;
2169}
2170
2171/* TODO: where to put this, it's actually part of sstrings2 (logically)?
2172 */
2173const char *strrstr2(const char *haystack, const char *needle) {
2174 int counter;
2175 size_t len;
2176 const char *prev_pos;
2177 const char *pos;
2178
2179 if (haystack == NULL || needle == NULL)
2180 return NULL;
2181
2182 if (haystack[0] == '\0' || needle[0] == '\0')
2183 return NULL;
2184
2185 counter = 0;
2186 prev_pos = NULL;
2187 pos = haystack;
2188 len = strlen(needle);
2189 for (;;) {
2190 pos = strstr(pos, needle);
2191 if (pos == NULL) {
2192 if (counter == 0)
2193 return NULL;
2194 return prev_pos;
2195 }
2196 counter++;
2197 prev_pos = pos;
2198 pos += len;
2199 if (*pos == '\0')
2200 return prev_pos;
2201 }
2202}
2203
2204int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
2205 char *first_line_end;
2206 char *p;
2207 size_t first_line_len;
2208 char *pp;
2209 const char *start;
2210 char *first_line_buf;
2211
2212 /* find last start of a new header */
2213 start = strrstr2(buf, "\r\nHTTP/");
2214 if (start != NULL) {
2215 start += 2;
2216 buf = start;
2217 }
2218
2219 first_line_end = strstr(buf, "\r\n");
2220 if (first_line_end == NULL)
2221 return -1;
2222
2223 first_line_len = (size_t)(first_line_end - buf);
2224 status_line->first_line = (char *)malloc(first_line_len + 1);
2225 if (status_line->first_line == NULL)
2226 return -1;
2227 memcpy(status_line->first_line, buf, first_line_len);
2228 status_line->first_line[first_line_len] = '\0';
2229 first_line_buf = strdup(status_line->first_line);
2230
2231 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
2232
2233 p = strtok(first_line_buf, "/");
2234 if (p == NULL) {
2235 free(first_line_buf);
2236 return -1;
2237 }
2238 if (strcmp(p, "HTTP") != 0) {
2239 free(first_line_buf);
2240 return -1;
2241 }
2242
2243 p = strtok(NULL, " ");
2244 if (p == NULL) {
2245 free(first_line_buf);
2246 return -1;
2247 }
2248 if (strchr(p, '.') != NULL) {
2249
2250 /* HTTP 1.x case */
2251 strtok(p, ".");
2252 status_line->http_major = (int)strtol(p, &pp, 10);
2253 if (*pp != '\0') {
2254 free(first_line_buf);
2255 return -1;
2256 }
2257 strtok(NULL, " ");
2258 status_line->http_minor = (int)strtol(p, &pp, 10);
2259 if (*pp != '\0') {
2260 free(first_line_buf);
2261 return -1;
2262 }
2263 p += 4; /* 1.x SP */
2264 } else {
2265 /* HTTP 2 case */
2266 status_line->http_major = (int)strtol(p, &pp, 10);
2267 status_line->http_minor = 0;
2268 p += 2; /* 2 SP */
2269 }
2270
2271 /* status code: "404" or "404.1", then SP */
2272
2273 p = strtok(p, " ");
2274 if (p == NULL) {
2275 free(first_line_buf);
2276 return -1;
2277 }
2278 if (strchr(p, '.') != NULL) {
2279 char *ppp;
2280 ppp = strtok(p, ".");
2281 status_line->http_code = (int)strtol(ppp, &pp, 10);
2282 if (*pp != '\0') {
2283 free(first_line_buf);
2284 return -1;
2285 }
2286 ppp = strtok(NULL, "");
2287 status_line->http_subcode = (int)strtol(ppp, &pp, 10);
2288 if (*pp != '\0') {
2289 free(first_line_buf);
2290 return -1;
2291 }
2292 p += 6; /* 400.1 SP */
2293 } else {
2294 status_line->http_code = (int)strtol(p, &pp, 10);
2295 status_line->http_subcode = -1;
2296 if (*pp != '\0') {
2297 free(first_line_buf);
2298 return -1;
2299 }
2300 p += 4; /* 400 SP */
2301 }
2302
2303 /* Human readable message: "Not Found" CRLF */
2304
2305 p = strtok(p, "");
2306 if (p == NULL) {
2307 status_line->msg = "";
2308 return 0;
2309 }
2310 status_line->msg = status_line->first_line + (p - first_line_buf);
2311 free(first_line_buf);
2312
2313 return 0;
2314}
2315
2316void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
2317
2318char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) {
2319 for (size_t i = 0; i < nof_headers; i++) {
2320 if (headers[i].name != NULL && strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
2321 return strndup(headers[i].value, headers[i].value_len);
2322 }
2323 }
2324 return NULL;
2325}
2326
2327int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) {
2328 char *server_date = NULL;
2329 char *document_date = NULL;
2330 int date_result = STATE_OK;
2331 curlhelp_statusline status_line;
2332 struct phr_header headers[255];
2333 size_t nof_headers = 255;
2334 size_t msglen;
2335
2336 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
2337 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
2338
2339 if (res == -1) {
2340 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2341 }
2342
2343 server_date = get_header_value(headers, nof_headers, "date");
2344 document_date = get_header_value(headers, nof_headers, "last-modified");
2345
2346 if (!server_date || !*server_date) {
2347 char tmp[DEFAULT_BUFFER_SIZE];
2348
2349 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
2350 strcpy(*msg, tmp);
2351
2352 date_result = max_state_alt(STATE_UNKNOWN, date_result);
2353
2354 } else if (!document_date || !*document_date) {
2355 char tmp[DEFAULT_BUFFER_SIZE];
2356
2357 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
2358 strcpy(*msg, tmp);
2359
2360 date_result = max_state_alt(STATE_CRITICAL, date_result);
2361
2362 } else {
2363 time_t srv_data = curl_getdate(server_date, NULL);
2364 time_t doc_data = curl_getdate(document_date, NULL);
2365 if (verbose >= 2)
2366 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data);
2367 if (srv_data <= 0) {
2368 char tmp[DEFAULT_BUFFER_SIZE];
2369
2370 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
2371 strcpy(*msg, tmp);
2372
2373 date_result = max_state_alt(STATE_CRITICAL, date_result);
2374 } else if (doc_data <= 0) {
2375 char tmp[DEFAULT_BUFFER_SIZE];
2376
2377 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
2378 strcpy(*msg, tmp);
2379
2380 date_result = max_state_alt(STATE_CRITICAL, date_result);
2381 } else if (doc_data > srv_data + 30) {
2382 char tmp[DEFAULT_BUFFER_SIZE];
2383
2384 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
2385 strcpy(*msg, tmp);
2386
2387 date_result = max_state_alt(STATE_CRITICAL, date_result);
2388 } else if (doc_data < srv_data - maximum_age) {
2389 int n = (srv_data - doc_data);
2390 if (n > (60 * 60 * 24 * 2)) {
2391 char tmp[DEFAULT_BUFFER_SIZE];
2392
2393 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float)n) / (60 * 60 * 24));
2394 strcpy(*msg, tmp);
2395
2396 date_result = max_state_alt(STATE_CRITICAL, date_result);
2397 } else {
2398 char tmp[DEFAULT_BUFFER_SIZE];
2399
2400 snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
2401 strcpy(*msg, tmp);
2402
2403 date_result = max_state_alt(STATE_CRITICAL, date_result);
2404 }
2405 }
2406 }
2407
2408 if (server_date)
2409 free(server_date);
2410 if (document_date)
2411 free(document_date);
2412
2413 return date_result;
2414}
2415
2416int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) {
2417 size_t content_length = 0;
2418 struct phr_header headers[255];
2419 size_t nof_headers = 255;
2420 size_t msglen;
2421 char *content_length_s = NULL;
2422 curlhelp_statusline status_line;
2423
2424 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor,
2425 &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0);
2426
2427 if (res == -1) {
2428 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
2429 }
2430
2431 content_length_s = get_header_value(headers, nof_headers, "content-length");
2432 if (!content_length_s) {
2433 return header_buf->buflen + body_buf->buflen;
2434 }
2435 content_length_s += strspn(content_length_s, " \t");
2436 content_length = atoi(content_length_s);
2437 if (content_length != body_buf->buflen) {
2438 /* TODO: should we warn if the actual and the reported body length don't match? */
2439 }
2440
2441 if (content_length_s)
2442 free(content_length_s);
2443
2444 return header_buf->buflen + body_buf->buflen;
2445}
2446
2447/* TODO: is there a better way in libcurl to check for the SSL library? */
2448curlhelp_ssl_library curlhelp_get_ssl_library(void) {
2449 curl_version_info_data *version_data;
2450 char *ssl_version;
2451 char *library;
2452 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
2453
2454 version_data = curl_version_info(CURLVERSION_NOW);
2455 if (version_data == NULL)
2456 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2457
2458 ssl_version = strdup(version_data->ssl_version);
2459 if (ssl_version == NULL)
2460 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2461
2462 library = strtok(ssl_version, "/");
2463 if (library == NULL)
2464 return CURLHELP_SSL_LIBRARY_UNKNOWN;
2465
2466 if (strcmp(library, "OpenSSL") == 0)
2467 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
2468 else if (strcmp(library, "LibreSSL") == 0)
2469 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
2470 else if (strcmp(library, "GnuTLS") == 0)
2471 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
2472 else if (strcmp(library, "NSS") == 0)
2473 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
2474
2475 if (verbose >= 2)
2476 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library);
2477
2478 free(ssl_version);
2479
2480 return ssl_library;
2481}
2482
2483const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) {
2484 switch (ssl_library) {
2485 case CURLHELP_SSL_LIBRARY_OPENSSL:
2486 return "OpenSSL";
2487 case CURLHELP_SSL_LIBRARY_LIBRESSL:
2488 return "LibreSSL";
2489 case CURLHELP_SSL_LIBRARY_GNUTLS:
2490 return "GnuTLS";
2491 case CURLHELP_SSL_LIBRARY_NSS:
2492 return "NSS";
2493 case CURLHELP_SSL_LIBRARY_UNKNOWN:
2494 default:
2495 return "unknown";
2496 }
2497}
2498
2499#ifdef LIBCURL_FEATURE_SSL 1729#ifdef LIBCURL_FEATURE_SSL
2500# ifndef USE_OPENSSL 1730# ifndef USE_OPENSSL
2501time_t parse_cert_date(const char *s) { 1731time_t parse_cert_date(const char *s) {
2502 struct tm tm; 1732 if (!s) {
2503 time_t date;
2504 char *res;
2505
2506 if (!s)
2507 return -1; 1733 return -1;
1734 }
2508 1735
2509 /* Jan 17 14:25:12 2020 GMT */ 1736 /* Jan 17 14:25:12 2020 GMT */
2510 res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); 1737 struct tm tm;
1738 char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2511 /* Sep 11 12:00:00 2020 GMT */ 1739 /* Sep 11 12:00:00 2020 GMT */
2512 if (res == NULL) 1740 if (res == NULL) {
2513 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); 1741 strptime(s, "%Y %m %d %H:%M:%S GMT", &tm);
2514 date = mktime(&tm); 1742 }
1743 time_t date = mktime(&tm);
2515 1744
2516 return date; 1745 return date;
2517} 1746}
1747# endif /* USE_OPENSSL */
1748#endif /* LIBCURL_FEATURE_SSL */
2518 1749
1750#ifdef LIBCURL_FEATURE_SSL
1751# ifndef USE_OPENSSL
2519/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to 1752/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
2520 * OpenSSL could be this function 1753 * OpenSSL could be this function
2521 */ 1754 */
2522int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { 1755int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn,
2523 int i; 1756 int days_till_exp_crit) {
2524 struct curl_slist *slist;
2525 int cname_found = 0;
2526 char *start_date_str = NULL;
2527 char *end_date_str = NULL;
2528 time_t start_date;
2529 time_t end_date;
2530 char *tz;
2531 float time_left;
2532 int days_left;
2533 int time_remaining;
2534 char timestamp[50] = "";
2535 int status = STATE_UNKNOWN;
2536 1757
2537 if (verbose >= 2) 1758 if (verbose >= 2) {
2538 printf("**** REQUEST CERTIFICATES ****\n"); 1759 printf("**** REQUEST CERTIFICATES ****\n");
1760 }
1761
1762 char *start_date_str = NULL;
1763 char *end_date_str = NULL;
1764 bool have_first_cert = false;
1765 bool cname_found = false;
1766 for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
1767 if (have_first_cert) {
1768 break;
1769 }
2539 1770
2540 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { 1771 struct curl_slist *slist;
2541 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { 1772 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2542 /* find first common name in subject, 1773 /* find first common name in subject,
2543 * TODO: check alternative subjects for 1774 * TODO: check alternative subjects for
@@ -2553,7 +1784,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_
2553 } 1784 }
2554 if (p != NULL) { 1785 if (p != NULL) {
2555 if (strncmp(host_name, p + d, strlen(host_name)) == 0) { 1786 if (strncmp(host_name, p + d, strlen(host_name)) == 0) {
2556 cname_found = 1; 1787 cname_found = true;
2557 } 1788 }
2558 } 1789 }
2559 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { 1790 } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) {
@@ -2561,78 +1792,93 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_
2561 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { 1792 } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) {
2562 end_date_str = &slist->data[12]; 1793 end_date_str = &slist->data[12];
2563 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { 1794 } else if (strncasecmp(slist->data, "Cert:", 5) == 0) {
2564 goto HAVE_FIRST_CERT; 1795 have_first_cert = true;
1796 break;
2565 } 1797 }
2566 if (verbose >= 2) 1798 if (verbose >= 2) {
2567 printf("%d ** %s\n", i, slist->data); 1799 printf("%d ** %s\n", i, slist->data);
1800 }
2568 } 1801 }
2569 } 1802 }
2570HAVE_FIRST_CERT:
2571 1803
2572 if (verbose >= 2) 1804 if (verbose >= 2) {
2573 printf("**** REQUEST CERTIFICATES ****\n"); 1805 printf("**** REQUEST CERTIFICATES ****\n");
1806 }
2574 1807
2575 if (!cname_found) { 1808 if (!cname_found) {
2576 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); 1809 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
2577 return STATE_CRITICAL; 1810 return STATE_CRITICAL;
2578 } 1811 }
2579 1812
2580 start_date = parse_cert_date(start_date_str); 1813 time_t start_date = parse_cert_date(start_date_str);
2581 if (start_date <= 0) { 1814 if (start_date <= 0) {
2582 snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); 1815 snprintf(msg, DEFAULT_BUFFER_SIZE,
1816 _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str);
2583 puts(msg); 1817 puts(msg);
2584 return STATE_WARNING; 1818 return STATE_WARNING;
2585 } 1819 }
2586 1820
2587 end_date = parse_cert_date(end_date_str); 1821 time_t end_date = parse_cert_date(end_date_str);
2588 if (end_date <= 0) { 1822 if (end_date <= 0) {
2589 snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); 1823 snprintf(msg, DEFAULT_BUFFER_SIZE,
1824 _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str);
2590 puts(msg); 1825 puts(msg);
2591 return STATE_WARNING; 1826 return STATE_WARNING;
2592 } 1827 }
2593 1828
2594 time_left = difftime(end_date, time(NULL)); 1829 float time_left = difftime(end_date, time(NULL));
2595 days_left = time_left / 86400; 1830 int days_left = time_left / 86400;
2596 tz = getenv("TZ"); 1831 char *tz = getenv("TZ");
2597 setenv("TZ", "GMT", 1); 1832 setenv("TZ", "GMT", 1);
2598 tzset(); 1833 tzset();
1834
1835 char timestamp[50] = "";
2599 strftime(timestamp, 50, "%c %z", localtime(&end_date)); 1836 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2600 if (tz) 1837 if (tz) {
2601 setenv("TZ", tz, 1); 1838 setenv("TZ", tz, 1);
2602 else 1839 } else {
2603 unsetenv("TZ"); 1840 unsetenv("TZ");
1841 }
2604 tzset(); 1842 tzset();
2605 1843
1844 mp_state_enum status = STATE_UNKNOWN;
1845 int time_remaining;
2606 if (days_left > 0 && days_left <= days_till_exp_warn) { 1846 if (days_left > 0 && days_left <= days_till_exp_warn) {
2607 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", 1847 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
2608 host_name, days_left, timestamp); 1848 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left,
2609 if (days_left > days_till_exp_crit) 1849 timestamp);
1850 if (days_left > days_till_exp_crit) {
2610 status = STATE_WARNING; 1851 status = STATE_WARNING;
2611 else 1852 } else {
2612 status = STATE_CRITICAL; 1853 status = STATE_CRITICAL;
1854 }
2613 } else if (days_left == 0 && time_left > 0) { 1855 } else if (days_left == 0 && time_left > 0) {
2614 if (time_left >= 3600) 1856 if (time_left >= 3600) {
2615 time_remaining = (int)time_left / 3600; 1857 time_remaining = (int)time_left / 3600;
2616 else 1858 } else {
2617 time_remaining = (int)time_left / 60; 1859 time_remaining = (int)time_left / 60;
1860 }
2618 1861
2619 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, 1862 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2620 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); 1863 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
1864 time_left >= 3600 ? "hours" : "minutes", timestamp);
2621 1865
2622 if (days_left > days_till_exp_crit) 1866 if (days_left > days_till_exp_crit) {
2623 status = STATE_WARNING; 1867 status = STATE_WARNING;
2624 else 1868 } else {
2625 status = STATE_CRITICAL; 1869 status = STATE_CRITICAL;
1870 }
2626 } else if (time_left < 0) { 1871 } else if (time_left < 0) {
2627 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); 1872 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2628 status = STATE_CRITICAL; 1873 status = STATE_CRITICAL;
2629 } else if (days_left == 0) { 1874 } else if (days_left == 0) {
2630 printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, 1875 printf(_("%s - Certificate '%s' just expired (%s).\n"),
2631 timestamp); 1876 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp);
2632 if (days_left > days_till_exp_crit) 1877 if (days_left > days_till_exp_crit) {
2633 status = STATE_WARNING; 1878 status = STATE_WARNING;
2634 else 1879 } else {
2635 status = STATE_CRITICAL; 1880 status = STATE_CRITICAL;
1881 }
2636 } else { 1882 } else {
2637 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); 1883 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
2638 status = STATE_OK; 1884 status = STATE_OK;
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
new file mode 100644
index 00000000..c3c2ba55
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -0,0 +1,1267 @@
1#include "./check_curl_helpers.h"
2#include <stdbool.h>
3#include <arpa/inet.h>
4#include <netinet/in.h>
5#include <netdb.h>
6#include <stdlib.h>
7#include "../utils.h"
8#include "check_curl.d/config.h"
9#include "output.h"
10#include "perfdata.h"
11#include "states.h"
12
13extern int verbose;
14char errbuf[MAX_INPUT_BUFFER];
15bool is_openssl_callback = false;
16bool add_sslctx_verify_fun = false;
17
18check_curl_configure_curl_wrapper
19check_curl_configure_curl(const check_curl_static_curl_config config,
20 check_curl_working_state working_state, bool check_cert,
21 bool on_redirect_dependent, int follow_method, int max_depth) {
22 check_curl_configure_curl_wrapper result = {
23 .errorcode = OK,
24 .curl_state =
25 {
26 .curl_global_initialized = false,
27 .curl_easy_initialized = false,
28 .curl = NULL,
29
30 .body_buf_initialized = false,
31 .body_buf = NULL,
32 .header_buf_initialized = false,
33 .header_buf = NULL,
34 .status_line_initialized = false,
35 .status_line = NULL,
36 .put_buf_initialized = false,
37 .put_buf = NULL,
38
39 .header_list = NULL,
40 .host = NULL,
41 },
42 };
43
44 if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) {
45 die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n");
46 }
47
48 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
49 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
50 }
51 result.curl_state.curl_global_initialized = true;
52
53 if ((result.curl_state.curl = curl_easy_init()) == NULL) {
54 die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
55 }
56 result.curl_state.curl_easy_initialized = true;
57
58 if (verbose >= 1) {
59 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1),
60 "CURLOPT_VERBOSE");
61 }
62
63 /* print everything on stdout like check_http would do */
64 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout),
65 "CURLOPT_STDERR");
66
67 if (config.automatic_decompression) {
68#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6)
69 handle_curl_option_return_code(
70 curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""),
71 "CURLOPT_ACCEPT_ENCODING");
72#else
73 handle_curl_option_return_code(
74 curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING");
75#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */
76 }
77
78 /* initialize buffer for body of the answer */
79 if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) {
80 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
81 }
82 result.curl_state.body_buf_initialized = true;
83
84 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION,
85 curlhelp_buffer_write_callback),
86 "CURLOPT_WRITEFUNCTION");
87 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA,
88 (void *)result.curl_state.body_buf),
89 "CURLOPT_WRITEDATA");
90
91 /* initialize buffer for header of the answer */
92 if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) {
93 die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n");
94 }
95 result.curl_state.header_buf_initialized = true;
96
97 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION,
98 curlhelp_buffer_write_callback),
99 "CURLOPT_HEADERFUNCTION");
100 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER,
101 (void *)result.curl_state.header_buf),
102 "CURLOPT_WRITEHEADER");
103
104 /* set the error buffer */
105 handle_curl_option_return_code(
106 curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf),
107 "CURLOPT_ERRORBUFFER");
108
109 /* set timeouts */
110 handle_curl_option_return_code(
111 curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout),
112 "CURLOPT_CONNECTTIMEOUT");
113
114 handle_curl_option_return_code(
115 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout),
116 "CURLOPT_TIMEOUT");
117
118 /* enable haproxy protocol */
119 if (config.haproxy_protocol) {
120 handle_curl_option_return_code(
121 curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L),
122 "CURLOPT_HAPROXYPROTOCOL");
123 }
124
125 // fill dns resolve cache to make curl connect to the given server_address instead of the
126 // host_name, only required for ssl, because we use the host_name later on to make SNI happy
127 char dnscache[DEFAULT_BUFFER_SIZE];
128 char addrstr[DEFAULT_BUFFER_SIZE / 2];
129 if (working_state.use_ssl && working_state.host_name != NULL) {
130 int res;
131 if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2,
132 config.sin_family)) != 0) {
133 die(STATE_CRITICAL,
134 _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"),
135 working_state.server_address, res, gai_strerror(res));
136 }
137
138 snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name,
139 working_state.serverPort, addrstr);
140 result.curl_state.host = curl_slist_append(NULL, dnscache);
141 curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host);
142
143 if (verbose >= 1) {
144 printf("* curl CURLOPT_RESOLVE: %s\n", dnscache);
145 }
146 }
147
148 // If server_address is an IPv6 address it must be surround by square brackets
149 struct in6_addr tmp_in_addr;
150 if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) {
151 char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char));
152 if (new_server_address == NULL) {
153 die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n");
154 }
155 snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]",
156 working_state.server_address);
157 working_state.server_address = new_server_address;
158 }
159
160 /* compose URL: use the address we want to connect to, set Host: header later */
161 char *url = fmt_url(working_state);
162
163 if (verbose >= 1) {
164 printf("* curl CURLOPT_URL: %s\n", url);
165 }
166 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url),
167 "CURLOPT_URL");
168
169 free(url);
170
171 /* extract proxy information for legacy proxy https requests */
172 if (!strcmp(working_state.http_method, "CONNECT") ||
173 strstr(working_state.server_url, "http") == working_state.server_url) {
174 handle_curl_option_return_code(
175 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address),
176 "CURLOPT_PROXY");
177 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT,
178 (long)working_state.serverPort),
179 "CURLOPT_PROXYPORT");
180 if (verbose >= 2) {
181 printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address,
182 working_state.serverPort);
183 }
184 working_state.http_method = "GET";
185 handle_curl_option_return_code(
186 curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url),
187 "CURLOPT_URL");
188 }
189
190 /* disable body for HEAD request */
191 if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) {
192 working_state.no_body = true;
193 }
194
195 /* set HTTP protocol version */
196 handle_curl_option_return_code(
197 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version),
198 "CURLOPT_HTTP_VERSION");
199
200 /* set HTTP method */
201 if (working_state.http_method) {
202 if (!strcmp(working_state.http_method, "POST")) {
203 handle_curl_option_return_code(
204 curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST");
205 } else if (!strcmp(working_state.http_method, "PUT")) {
206 handle_curl_option_return_code(
207 curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
208 } else {
209 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
210 CURLOPT_CUSTOMREQUEST,
211 working_state.http_method),
212 "CURLOPT_CUSTOMREQUEST");
213 }
214 }
215
216 char *force_host_header = NULL;
217 /* check if Host header is explicitly set in options */
218 if (config.http_opt_headers_count) {
219 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
220 if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) {
221 force_host_header = config.http_opt_headers[i];
222 }
223 }
224 }
225
226 /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in
227 * anyway */
228 char http_header[DEFAULT_BUFFER_SIZE];
229 if (working_state.host_name != NULL && force_host_header == NULL) {
230 if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) ||
231 (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) {
232 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name,
233 working_state.virtualPort);
234 } else {
235 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name);
236 }
237 result.curl_state.header_list =
238 curl_slist_append(result.curl_state.header_list, http_header);
239 }
240
241 /* always close connection, be nice to servers */
242 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
243 result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header);
244
245 /* attach additional headers supplied by the user */
246 /* optionally send any other header tag */
247 if (config.http_opt_headers_count) {
248 for (size_t i = 0; i < config.http_opt_headers_count; i++) {
249 result.curl_state.header_list =
250 curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]);
251 }
252 }
253
254 /* set HTTP headers */
255 handle_curl_option_return_code(
256 curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list),
257 "CURLOPT_HTTPHEADER");
258
259#ifdef LIBCURL_FEATURE_SSL
260 /* set SSL version, warn about insecure or unsupported versions */
261 if (working_state.use_ssl) {
262 handle_curl_option_return_code(
263 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version),
264 "CURLOPT_SSLVERSION");
265 }
266
267 /* client certificate and key to present to server (SSL) */
268 if (config.client_cert) {
269 handle_curl_option_return_code(
270 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert),
271 "CURLOPT_SSLCERT");
272 }
273
274 if (config.client_privkey) {
275 handle_curl_option_return_code(
276 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey),
277 "CURLOPT_SSLKEY");
278 }
279
280 if (config.ca_cert) {
281 handle_curl_option_return_code(
282 curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert),
283 "CURLOPT_CAINFO");
284 }
285
286 if (config.ca_cert || config.verify_peer_and_host) {
287 /* per default if we have a CA verify both the peer and the
288 * hostname in the certificate, can be switched off later */
289 handle_curl_option_return_code(
290 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1),
291 "CURLOPT_SSL_VERIFYPEER");
292 handle_curl_option_return_code(
293 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2),
294 "CURLOPT_SSL_VERIFYHOST");
295 } else {
296 /* backward-compatible behaviour, be tolerant in checks
297 * TODO: depending on more options have aspects we want
298 * to be less tolerant about ssl verfications
299 */
300 handle_curl_option_return_code(
301 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0),
302 "CURLOPT_SSL_VERIFYPEER");
303 handle_curl_option_return_code(
304 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0),
305 "CURLOPT_SSL_VERIFYHOST");
306 }
307
308 /* detect SSL library used by libcurl */
309 curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library();
310
311 /* try hard to get a stack of certificates to verify against */
312 if (check_cert) {
313# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
314 /* inform curl to report back certificates */
315 switch (ssl_library) {
316 case CURLHELP_SSL_LIBRARY_OPENSSL:
317 case CURLHELP_SSL_LIBRARY_LIBRESSL:
318 /* set callback to extract certificate with OpenSSL context function (works with
319 * OpenSSL-style libraries only!) */
320# ifdef USE_OPENSSL
321 /* libcurl and monitoring plugins built with OpenSSL, good */
322 add_sslctx_verify_fun = true;
323 is_openssl_callback = true;
324# endif /* USE_OPENSSL */
325 /* libcurl is built with OpenSSL, monitoring plugins, so falling
326 * back to manually extracting certificate information */
327 handle_curl_option_return_code(
328 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
329 break;
330
331 case CURLHELP_SSL_LIBRARY_NSS:
332# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
333 /* NSS: support for CERTINFO is implemented since 7.34.0 */
334 handle_curl_option_return_code(
335 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
336# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
337 die(STATE_CRITICAL,
338 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
339 "'%s' is too old)\n",
340 curlhelp_get_ssl_library_string(ssl_library));
341# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
342 break;
343
344 case CURLHELP_SSL_LIBRARY_GNUTLS:
345# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
346 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
347 handle_curl_option_return_code(
348 curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
349# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
350 die(STATE_CRITICAL,
351 "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library "
352 "'%s' is too old)\n",
353 curlhelp_get_ssl_library_string(ssl_library));
354# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
355 break;
356
357 case CURLHELP_SSL_LIBRARY_UNKNOWN:
358 default:
359 die(STATE_CRITICAL,
360 "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must "
361 "implement first)\n",
362 curlhelp_get_ssl_library_string(ssl_library));
363 break;
364 }
365# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
366 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
367 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL ||
368 ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) {
369 add_sslctx_verify_fun = true;
370 } else {
371 die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no "
372 "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl "
373 "too old and has no CURLOPT_CERTINFO)\n");
374 }
375# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
376 }
377
378# if LIBCURL_VERSION_NUM >= \
379 MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */
380 // ssl ctx function is not available with all ssl backends
381 if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) !=
382 CURLE_UNKNOWN_OPTION) {
383 handle_curl_option_return_code(
384 curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun),
385 "CURLOPT_SSL_CTX_FUNCTION");
386 }
387# endif
388#endif /* LIBCURL_FEATURE_SSL */
389
390 /* set default or user-given user agent identification */
391 handle_curl_option_return_code(
392 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent),
393 "CURLOPT_USERAGENT");
394
395 /* proxy-authentication */
396 if (strcmp(config.proxy_auth, "")) {
397 handle_curl_option_return_code(
398 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth),
399 "CURLOPT_PROXYUSERPWD");
400 }
401
402 /* authentication */
403 if (strcmp(config.user_auth, "")) {
404 handle_curl_option_return_code(
405 curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth),
406 "CURLOPT_USERPWD");
407 }
408 /* TODO: parameter auth method, bitfield of following methods:
409 * CURLAUTH_BASIC (default)
410 * CURLAUTH_DIGEST
411 * CURLAUTH_DIGEST_IE
412 * CURLAUTH_NEGOTIATE
413 * CURLAUTH_NTLM
414 * CURLAUTH_NTLM_WB
415 *
416 * convenience tokens for typical sets of methods:
417 * CURLAUTH_ANYSAFE: most secure, without BASIC
418 * or CURLAUTH_ANY: most secure, even BASIC if necessary
419 *
420 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH,
421 * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
422 */
423
424 /* handle redirections */
425 if (on_redirect_dependent) {
426 if (follow_method == FOLLOW_LIBCURL) {
427 handle_curl_option_return_code(
428 curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1),
429 "CURLOPT_FOLLOWLOCATION");
430
431 /* default -1 is infinite, not good, could lead to zombie plugins!
432 Setting it to one bigger than maximal limit to handle errors nicely below
433 */
434 handle_curl_option_return_code(
435 curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1),
436 "CURLOPT_MAXREDIRS");
437
438 /* for now allow only http and https (we are a http(s) check plugin in the end) */
439#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0)
440 handle_curl_option_return_code(
441 curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"),
442 "CURLOPT_REDIR_PROTOCOLS_STR");
443#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
444 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
445 CURLOPT_REDIR_PROTOCOLS,
446 CURLPROTO_HTTP | CURLPROTO_HTTPS),
447 "CURLOPT_REDIRECT_PROTOCOLS");
448#endif
449
450 /* TODO: handle the following aspects of redirection, make them
451 * command line options too later:
452 CURLOPT_POSTREDIR: method switch
453 CURLINFO_REDIRECT_URL: custom redirect option
454 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
455 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range
456 option here is nice like for expected page size?
457 */
458 } else {
459 /* old style redirection*/
460 }
461 }
462 /* no-body */
463 if (working_state.no_body) {
464 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1),
465 "CURLOPT_NOBODY");
466 }
467
468 /* IPv4 or IPv6 forced DNS resolution */
469 if (config.sin_family == AF_UNSPEC) {
470 handle_curl_option_return_code(
471 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER),
472 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
473 } else if (config.sin_family == AF_INET) {
474 handle_curl_option_return_code(
475 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
476 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
477 }
478#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6)
479 else if (config.sin_family == AF_INET6) {
480 handle_curl_option_return_code(
481 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
482 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
483 }
484#endif
485
486 /* either send http POST data (any data, not only POST)*/
487 if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) {
488 /* set content of payload for POST and PUT */
489 if (config.http_content_type) {
490 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s",
491 config.http_content_type);
492 result.curl_state.header_list =
493 curl_slist_append(result.curl_state.header_list, http_header);
494 }
495 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
496 * in case of no POST/PUT data */
497 if (!working_state.http_post_data) {
498 working_state.http_post_data = "";
499 }
500
501 if (!strcmp(working_state.http_method, "POST")) {
502 /* POST method, set payload with CURLOPT_POSTFIELDS */
503 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
504 CURLOPT_POSTFIELDS,
505 working_state.http_post_data),
506 "CURLOPT_POSTFIELDS");
507 } else if (!strcmp(working_state.http_method, "PUT")) {
508 handle_curl_option_return_code(
509 curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION,
510 (curl_read_callback)curlhelp_buffer_read_callback),
511 "CURLOPT_READFUNCTION");
512 if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data,
513 strlen(working_state.http_post_data)) < 0) {
514 die(STATE_UNKNOWN,
515 "HTTP CRITICAL - out of memory allocating read buffer for PUT\n");
516 }
517 result.curl_state.put_buf_initialized = true;
518 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl,
519 CURLOPT_READDATA,
520 (void *)result.curl_state.put_buf),
521 "CURLOPT_READDATA");
522 handle_curl_option_return_code(
523 curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE,
524 (curl_off_t)strlen(working_state.http_post_data)),
525 "CURLOPT_INFILESIZE");
526 }
527 }
528
529 /* cookie handling */
530 if (config.cookie_jar_file != NULL) {
531 /* enable reading cookies from a file, and if the filename is an empty string, only
532 * enable the curl cookie engine */
533 handle_curl_option_return_code(
534 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file),
535 "CURLOPT_COOKIEFILE");
536 /* now enable saving cookies to a file, but only if the filename is not an empty string,
537 * since writing it would fail */
538 if (*config.cookie_jar_file) {
539 handle_curl_option_return_code(
540 curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file),
541 "CURLOPT_COOKIEJAR");
542 }
543 }
544
545 result.working_state = working_state;
546
547 return result;
548}
549
550void handle_curl_option_return_code(CURLcode res, const char *option) {
551 if (res != CURLE_OK) {
552 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"),
553 option, res, curl_easy_strerror(res));
554 }
555}
556
557char *get_header_value(const struct phr_header *headers, const size_t nof_headers,
558 const char *header) {
559 for (size_t i = 0; i < nof_headers; i++) {
560 if (headers[i].name != NULL &&
561 strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) {
562 return strndup(headers[i].value, headers[i].value_len);
563 }
564 }
565 return NULL;
566}
567
568check_curl_working_state check_curl_working_state_init() {
569 check_curl_working_state result = {
570 .server_address = NULL,
571 .server_url = DEFAULT_SERVER_URL,
572 .host_name = NULL,
573 .http_method = NULL,
574 .http_post_data = NULL,
575 .virtualPort = 0,
576 .serverPort = HTTP_PORT,
577 .use_ssl = false,
578 .no_body = false,
579 };
580 return result;
581}
582
583check_curl_config check_curl_config_init() {
584 check_curl_config tmp = {
585 .initial_config = check_curl_working_state_init(),
586
587 .curl_config =
588 {
589 .automatic_decompression = false,
590 .socket_timeout = DEFAULT_SOCKET_TIMEOUT,
591 .haproxy_protocol = false,
592 .sin_family = AF_UNSPEC,
593 .curl_http_version = CURL_HTTP_VERSION_NONE,
594 .http_opt_headers = NULL,
595 .http_opt_headers_count = 0,
596 .ssl_version = CURL_SSLVERSION_DEFAULT,
597 .client_cert = NULL,
598 .client_privkey = NULL,
599 .ca_cert = NULL,
600 .verify_peer_and_host = false,
601 .user_agent = {'\0'},
602 .proxy_auth = "",
603 .user_auth = "",
604 .http_content_type = NULL,
605 .cookie_jar_file = NULL,
606 },
607 .max_depth = DEFAULT_MAX_REDIRS,
608 .followmethod = FOLLOW_HTTP_CURL,
609 .followsticky = STICKY_NONE,
610
611 .maximum_age = -1,
612 .regexp = {},
613 .compiled_regex = {},
614 .state_regex = STATE_CRITICAL,
615 .invert_regex = false,
616 .check_cert = false,
617 .continue_after_check_cert = false,
618 .days_till_exp_warn = 0,
619 .days_till_exp_crit = 0,
620 .thlds = mp_thresholds_init(),
621 .page_length_limits = mp_range_init(),
622 .page_length_limits_is_set = false,
623 .server_expect =
624 {
625 .string = HTTP_EXPECT,
626 .is_present = false,
627 },
628 .string_expect = "",
629 .header_expect = "",
630 .on_redirect_result_state = STATE_OK,
631 .on_redirect_dependent = false,
632
633 .show_extended_perfdata = false,
634 .show_body = false,
635
636 .output_format_is_set = false,
637 };
638
639 snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)",
640 "check_curl", NP_VERSION, VERSION, curl_version());
641
642 return tmp;
643}
644
645/* TODO: is there a better way in libcurl to check for the SSL library? */
646curlhelp_ssl_library curlhelp_get_ssl_library(void) {
647 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
648
649 curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW);
650 if (version_data == NULL) {
651 return CURLHELP_SSL_LIBRARY_UNKNOWN;
652 }
653
654 char *ssl_version = strdup(version_data->ssl_version);
655 if (ssl_version == NULL) {
656 return CURLHELP_SSL_LIBRARY_UNKNOWN;
657 }
658
659 char *library = strtok(ssl_version, "/");
660 if (library == NULL) {
661 return CURLHELP_SSL_LIBRARY_UNKNOWN;
662 }
663
664 if (strcmp(library, "OpenSSL") == 0) {
665 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
666 } else if (strcmp(library, "LibreSSL") == 0) {
667 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
668 } else if (strcmp(library, "GnuTLS") == 0) {
669 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
670 } else if (strcmp(library, "NSS") == 0) {
671 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
672 }
673
674 if (verbose >= 2) {
675 printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library,
676 ssl_library);
677 }
678
679 free(ssl_version);
680
681 return ssl_library;
682}
683
684const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) {
685 switch (ssl_library) {
686 case CURLHELP_SSL_LIBRARY_OPENSSL:
687 return "OpenSSL";
688 case CURLHELP_SSL_LIBRARY_LIBRESSL:
689 return "LibreSSL";
690 case CURLHELP_SSL_LIBRARY_GNUTLS:
691 return "GnuTLS";
692 case CURLHELP_SSL_LIBRARY_NSS:
693 return "NSS";
694 case CURLHELP_SSL_LIBRARY_UNKNOWN:
695 default:
696 return "unknown";
697 }
698}
699
700size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
701 const curlhelp_write_curlbuf *body_buf) {
702 struct phr_header headers[255];
703 size_t nof_headers = 255;
704 size_t msglen;
705 curlhelp_statusline status_line;
706 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
707 &status_line.http_minor, &status_line.http_code, &status_line.msg,
708 &msglen, headers, &nof_headers, 0);
709
710 if (res == -1) {
711 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
712 }
713
714 char *content_length_s = get_header_value(headers, nof_headers, "content-length");
715 if (!content_length_s) {
716 return header_buf->buflen + body_buf->buflen;
717 }
718
719 content_length_s += strspn(content_length_s, " \t");
720 size_t content_length = atoi(content_length_s);
721 if (content_length != body_buf->buflen) {
722 /* TODO: should we warn if the actual and the reported body length don't match? */
723 }
724
725 if (content_length_s) {
726 free(content_length_s);
727 }
728
729 return header_buf->buflen + body_buf->buflen;
730}
731
732mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) {
733 struct phr_header headers[255];
734 size_t nof_headers = 255;
735 curlhelp_statusline status_line;
736 size_t msglen;
737 int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major,
738 &status_line.http_minor, &status_line.http_code, &status_line.msg,
739 &msglen, headers, &nof_headers, 0);
740
741 if (res == -1) {
742 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n"));
743 }
744
745 char *server_date = get_header_value(headers, nof_headers, "date");
746 char *document_date = get_header_value(headers, nof_headers, "last-modified");
747
748 mp_subcheck sc_document_dates = mp_subcheck_init();
749 if (!server_date || !*server_date) {
750 xasprintf(&sc_document_dates.output, _("Server date unknown"));
751 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN);
752 } else if (!document_date || !*document_date) {
753 xasprintf(&sc_document_dates.output, _("Document modification date unknown, "));
754 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
755 } else {
756 time_t srv_data = curl_getdate(server_date, NULL);
757 time_t doc_data = curl_getdate(document_date, NULL);
758
759 if (verbose >= 2) {
760 printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data,
761 document_date, (int)doc_data);
762 }
763
764 if (srv_data <= 0) {
765 xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"),
766 server_date);
767 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
768 } else if (doc_data <= 0) {
769
770 xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"),
771 document_date);
772 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
773 } else if (doc_data > srv_data + 30) {
774
775 xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"),
776 (int)doc_data - (int)srv_data);
777
778 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
779 } else if (doc_data < srv_data - maximum_age) {
780 time_t last_modified = (srv_data - doc_data);
781 if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded?
782 xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"),
783 ((float)last_modified) / (60 * 60 * 24));
784 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
785 } else {
786 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
787 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
788 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL);
789 }
790 } else {
791 // TODO is this the OK case?
792 time_t last_modified = (srv_data - doc_data);
793 xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"),
794 last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60);
795 sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK);
796 }
797 }
798
799 if (server_date) {
800 free(server_date);
801 }
802 if (document_date) {
803 free(document_date);
804 }
805
806 return sc_document_dates;
807}
808
809void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); }
810
811int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) {
812 /* find last start of a new header */
813 const char *start = strrstr2(buf, "\r\nHTTP/");
814 if (start != NULL) {
815 start += 2;
816 buf = start;
817 }
818
819 char *first_line_end = strstr(buf, "\r\n");
820 if (first_line_end == NULL) {
821 return -1;
822 }
823
824 size_t first_line_len = (size_t)(first_line_end - buf);
825 status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char));
826 if (status_line->first_line == NULL) {
827 return -1;
828 }
829 memcpy(status_line->first_line, buf, first_line_len);
830 status_line->first_line[first_line_len] = '\0';
831 char *first_line_buf = strdup(status_line->first_line);
832
833 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
834 char *temp_string = strtok(first_line_buf, "/");
835 if (temp_string == NULL) {
836 free(first_line_buf);
837 return -1;
838 }
839 if (strcmp(temp_string, "HTTP") != 0) {
840 free(first_line_buf);
841 return -1;
842 }
843
844 temp_string = strtok(NULL, " ");
845 if (temp_string == NULL) {
846 free(first_line_buf);
847 return -1;
848 }
849
850 char *temp_string_2;
851 if (strchr(temp_string, '.') != NULL) {
852
853 /* HTTP 1.x case */
854 strtok(temp_string, ".");
855 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
856 if (*temp_string_2 != '\0') {
857 free(first_line_buf);
858 return -1;
859 }
860 strtok(NULL, " ");
861 status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10);
862 if (*temp_string_2 != '\0') {
863 free(first_line_buf);
864 return -1;
865 }
866 temp_string += 4; /* 1.x SP */
867 } else {
868 /* HTTP 2 case */
869 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
870 status_line->http_minor = 0;
871 temp_string += 2; /* 2 SP */
872 }
873
874 /* status code: "404" or "404.1", then SP */
875 temp_string = strtok(temp_string, " ");
876 if (temp_string == NULL) {
877 free(first_line_buf);
878 return -1;
879 }
880 if (strchr(temp_string, '.') != NULL) {
881 char *ppp;
882 ppp = strtok(temp_string, ".");
883 status_line->http_code = (int)strtol(ppp, &temp_string_2, 10);
884 if (*temp_string_2 != '\0') {
885 free(first_line_buf);
886 return -1;
887 }
888 ppp = strtok(NULL, "");
889 status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10);
890 if (*temp_string_2 != '\0') {
891 free(first_line_buf);
892 return -1;
893 }
894 temp_string += 6; /* 400.1 SP */
895 } else {
896 status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10);
897 status_line->http_subcode = -1;
898 if (*temp_string_2 != '\0') {
899 free(first_line_buf);
900 return -1;
901 }
902 temp_string += 4; /* 400 SP */
903 }
904
905 /* Human readable message: "Not Found" CRLF */
906
907 temp_string = strtok(temp_string, "");
908 if (temp_string == NULL) {
909 status_line->msg = "";
910 return 0;
911 }
912 status_line->msg = status_line->first_line + (temp_string - first_line_buf);
913 free(first_line_buf);
914
915 return 0;
916}
917
918/* TODO: where to put this, it's actually part of sstrings2 (logically)?
919 */
920const char *strrstr2(const char *haystack, const char *needle) {
921 if (haystack == NULL || needle == NULL) {
922 return NULL;
923 }
924
925 if (haystack[0] == '\0' || needle[0] == '\0') {
926 return NULL;
927 }
928
929 int counter = 0;
930 const char *prev_pos = NULL;
931 const char *pos = haystack;
932 size_t len = strlen(needle);
933 for (;;) {
934 pos = strstr(pos, needle);
935 if (pos == NULL) {
936 if (counter == 0) {
937 return NULL;
938 }
939 return prev_pos;
940 }
941 counter++;
942 prev_pos = pos;
943 pos += len;
944 if (*pos == '\0') {
945 return prev_pos;
946 }
947 }
948}
949
950void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) {
951 free(buf->buf);
952 buf->buf = NULL;
953}
954
955void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) {
956 free(buf->buf);
957 buf->buf = NULL;
958}
959
960int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) {
961 if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) {
962 return 1;
963 }
964
965 (*buf)->buflen = datalen;
966 (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char));
967 if ((*buf)->buf == NULL) {
968 return -1;
969 }
970 memcpy((*buf)->buf, data, datalen);
971 (*buf)->pos = 0;
972 return 0;
973}
974
975size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
976 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
977
978 size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos);
979
980 memcpy(buffer, buf->buf + buf->pos, minimalSize);
981 buf->pos += minimalSize;
982
983 return minimalSize;
984}
985
986int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) {
987 if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) {
988 return 1;
989 }
990 (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char);
991 (*buf)->buflen = 0;
992 (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char));
993 if ((*buf)->buf == NULL) {
994 return -1;
995 }
996 return 0;
997}
998
999size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
1000 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1001
1002 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1003 buf->bufsize = buf->bufsize * 2;
1004 buf->buf = (char *)realloc(buf->buf, buf->bufsize);
1005 if (buf->buf == NULL) {
1006 fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno));
1007 return 0;
1008 }
1009 }
1010
1011 memcpy(buf->buf + buf->buflen, buffer, size * nmemb);
1012 buf->buflen += size * nmemb;
1013 buf->buf[buf->buflen] = '\0';
1014
1015 return size * nmemb;
1016}
1017
1018void cleanup(check_curl_global_state global_state) {
1019 if (global_state.status_line_initialized) {
1020 curlhelp_free_statusline(global_state.status_line);
1021 }
1022 global_state.status_line_initialized = false;
1023
1024 if (global_state.curl_easy_initialized) {
1025 curl_easy_cleanup(global_state.curl);
1026 }
1027 global_state.curl_easy_initialized = false;
1028
1029 if (global_state.curl_global_initialized) {
1030 curl_global_cleanup();
1031 }
1032 global_state.curl_global_initialized = false;
1033
1034 if (global_state.body_buf_initialized) {
1035 curlhelp_freewritebuffer(global_state.body_buf);
1036 }
1037 global_state.body_buf_initialized = false;
1038
1039 if (global_state.header_buf_initialized) {
1040 curlhelp_freewritebuffer(global_state.header_buf);
1041 }
1042 global_state.header_buf_initialized = false;
1043
1044 if (global_state.put_buf_initialized) {
1045 curlhelp_freereadbuffer(global_state.put_buf);
1046 }
1047 global_state.put_buf_initialized = false;
1048
1049 if (global_state.header_list) {
1050 curl_slist_free_all(global_state.header_list);
1051 }
1052
1053 if (global_state.host) {
1054 curl_slist_free_all(global_state.host);
1055 }
1056}
1057
1058int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) {
1059 struct addrinfo hints = {
1060 .ai_family = addr_family,
1061 .ai_socktype = SOCK_STREAM,
1062 .ai_flags = AI_CANONNAME,
1063 };
1064
1065 struct addrinfo *result;
1066 int errcode = getaddrinfo(host, NULL, &hints, &result);
1067 if (errcode != 0) {
1068 return errcode;
1069 }
1070
1071 strcpy(buf, "");
1072 struct addrinfo *res = result;
1073
1074 size_t buflen_remaining = buflen - 1;
1075 size_t addrstr_len;
1076 char addrstr[100];
1077 void *ptr = {0};
1078 while (res) {
1079 switch (res->ai_family) {
1080 case AF_INET:
1081 ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1082 break;
1083 case AF_INET6:
1084 ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1085 break;
1086 }
1087
1088 inet_ntop(res->ai_family, ptr, addrstr, 100);
1089 if (verbose >= 1) {
1090 printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4,
1091 addrstr);
1092 }
1093
1094 // Append all IPs to buf as a comma-separated string
1095 addrstr_len = strlen(addrstr);
1096 if (buflen_remaining > addrstr_len + 1) {
1097 if (buf[0] != '\0') {
1098 strncat(buf, ",", buflen_remaining);
1099 buflen_remaining -= 1;
1100 }
1101 strncat(buf, addrstr, buflen_remaining);
1102 buflen_remaining -= addrstr_len;
1103 }
1104
1105 res = res->ai_next;
1106 }
1107
1108 freeaddrinfo(result);
1109
1110 return 0;
1111}
1112
1113/* Checks if the server 'reply' is one of the expected 'statuscodes' */
1114bool expected_statuscode(const char *reply, const char *statuscodes) {
1115 char *expected;
1116
1117 if ((expected = strdup(statuscodes)) == NULL) {
1118 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
1119 }
1120
1121 bool result = false;
1122 for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
1123 if (strstr(reply, code) != NULL) {
1124 result = true;
1125 break;
1126 }
1127 }
1128
1129 free(expected);
1130 return result;
1131}
1132
1133/* returns a string "HTTP/1.x" or "HTTP/2" */
1134char *string_statuscode(int major, int minor) {
1135 static char buf[10];
1136
1137 switch (major) {
1138 case 1:
1139 snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor);
1140 break;
1141 case 2:
1142 case 3:
1143 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1144 break;
1145 default:
1146 /* assuming here HTTP/N with N>=4 */
1147 snprintf(buf, sizeof(buf), "HTTP/%d", major);
1148 break;
1149 }
1150
1151 return buf;
1152}
1153
1154/* check whether a file exists */
1155void test_file(char *path) {
1156 if (access(path, R_OK) == 0) {
1157 return;
1158 }
1159 usage2(_("file does not exist or is not readable"), path);
1160}
1161
1162mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
1163 int days_till_exp_crit);
1164
1165mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
1166 int crit_days_till_exp) {
1167 mp_subcheck sc_cert_result = mp_subcheck_init();
1168 sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK);
1169
1170#ifdef LIBCURL_FEATURE_SSL
1171 if (is_openssl_callback) {
1172# ifdef USE_OPENSSL
1173 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
1174 * and we actually have OpenSSL in the monitoring tools
1175 */
1176 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1177# else /* USE_OPENSSL */
1178 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
1179 "callback used and not linked against OpenSSL\n");
1180 mp_set_subcheck_state(result, STATE_CRITICAL);
1181# endif /* USE_OPENSSL */
1182 } else {
1183 struct curl_slist *slist;
1184
1185 cert_ptr_union cert_ptr = {0};
1186 cert_ptr.to_info = NULL;
1187 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
1188 if (!res && cert_ptr.to_info) {
1189# ifdef USE_OPENSSL
1190 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
1191 * parsing We only check the first certificate and assume it's the one of
1192 * the server
1193 */
1194 char *raw_cert = NULL;
1195 bool got_first_cert = false;
1196 for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
1197 if (got_first_cert) {
1198 break;
1199 }
1200
1201 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
1202 if (verbose >= 2) {
1203 printf("%d ** %s\n", i, slist->data);
1204 }
1205 if (strncmp(slist->data, "Cert:", 5) == 0) {
1206 raw_cert = &slist->data[5];
1207 got_first_cert = true;
1208 break;
1209 }
1210 }
1211 }
1212
1213 if (!raw_cert) {
1214
1215 xasprintf(&sc_cert_result.output,
1216 _("Cannot retrieve certificates from CERTINFO information - "
1217 "certificate data was empty"));
1218 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1219 return sc_cert_result;
1220 }
1221
1222 BIO *cert_BIO = BIO_new(BIO_s_mem());
1223 BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert));
1224
1225 cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL);
1226 if (!cert) {
1227 xasprintf(&sc_cert_result.output,
1228 _("Cannot read certificate from CERTINFO information - BIO error"));
1229 sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1230 return sc_cert_result;
1231 }
1232
1233 BIO_free(cert_BIO);
1234 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1235# else /* USE_OPENSSL */
1236 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
1237 * disposal, so we use the libcurl CURLINFO data
1238 */
1239 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
1240 days_till_exp_crit);
1241# endif /* USE_OPENSSL */
1242 } else {
1243 xasprintf(&sc_cert_result.output,
1244 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
1245 curl_easy_strerror(res));
1246 mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL);
1247 }
1248 }
1249#endif /* LIBCURL_FEATURE_SSL */
1250
1251 return sc_cert_result;
1252}
1253
1254char *fmt_url(check_curl_working_state workingState) {
1255 char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char));
1256 if (url == NULL) {
1257 die(STATE_UNKNOWN, "memory allocation failed");
1258 }
1259
1260 snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http",
1261 (workingState.use_ssl & (workingState.host_name != NULL))
1262 ? workingState.host_name
1263 : workingState.server_address,
1264 workingState.serverPort, workingState.server_url);
1265
1266 return url;
1267}
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
new file mode 100644
index 00000000..87e45a9d
--- /dev/null
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -0,0 +1,124 @@
1#include "./config.h"
2#include <curl/curl.h>
3#include "../picohttpparser/picohttpparser.h"
4#include "output.h"
5
6#if defined(HAVE_SSL) && defined(USE_OPENSSL)
7# include <openssl/opensslv.h>
8#endif
9
10/* for buffers for header and body */
11typedef struct {
12 size_t buflen;
13 size_t bufsize;
14 char *buf;
15} curlhelp_write_curlbuf;
16
17/* for buffering the data sent in PUT */
18typedef struct {
19 size_t buflen;
20 off_t pos;
21 char *buf;
22} curlhelp_read_curlbuf;
23
24/* for parsing the HTTP status line */
25typedef struct {
26 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
27 * never reached the big internet most likely) */
28 int http_minor; /* minor version of the protocol, usually 0 or 1 */
29 int http_code; /* HTTP return code as in RFC 2145 */
30 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
31 * http://support.microsoft.com/kb/318380/en-us */
32 const char *msg; /* the human readable message */
33 char *first_line; /* a copy of the first line */
34} curlhelp_statusline;
35
36typedef struct {
37 bool curl_global_initialized;
38 bool curl_easy_initialized;
39
40 bool body_buf_initialized;
41 curlhelp_write_curlbuf *body_buf;
42
43 bool header_buf_initialized;
44 curlhelp_write_curlbuf *header_buf;
45
46 bool status_line_initialized;
47 curlhelp_statusline *status_line;
48
49 bool put_buf_initialized;
50 curlhelp_read_curlbuf *put_buf;
51
52 CURL *curl;
53
54 struct curl_slist *header_list;
55 struct curl_slist *host;
56} check_curl_global_state;
57
58/* to know the underlying SSL library used by libcurl */
59typedef enum curlhelp_ssl_library {
60 CURLHELP_SSL_LIBRARY_UNKNOWN,
61 CURLHELP_SSL_LIBRARY_OPENSSL,
62 CURLHELP_SSL_LIBRARY_LIBRESSL,
63 CURLHELP_SSL_LIBRARY_GNUTLS,
64 CURLHELP_SSL_LIBRARY_NSS
65} curlhelp_ssl_library;
66
67#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch))
68
69typedef struct {
70 int errorcode;
71 check_curl_global_state curl_state;
72 check_curl_working_state working_state;
73} check_curl_configure_curl_wrapper;
74
75check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config,
76 check_curl_working_state working_state,
77 bool check_cert,
78 bool on_redirect_dependent,
79 int follow_method, int max_depth);
80
81void handle_curl_option_return_code(CURLcode res, const char *option);
82
83int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf);
84size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
85 void * /*stream*/);
86void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/);
87
88int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/);
89size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/,
90 void * /*stream*/);
91void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/);
92
93curlhelp_ssl_library curlhelp_get_ssl_library(void);
94const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/);
95
96typedef union {
97 struct curl_slist *to_info;
98 struct curl_certinfo *to_certinfo;
99} cert_ptr_union;
100int net_noopenssl_check_certificate(cert_ptr_union *, int, int);
101
102int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/);
103void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/);
104
105char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header);
106mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/,
107 int /*maximum_age*/);
108size_t get_content_length(const curlhelp_write_curlbuf *header_buf,
109 const curlhelp_write_curlbuf *body_buf);
110int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family);
111CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm);
112
113#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
114const char *strrstr2(const char *haystack, const char *needle);
115
116void cleanup(check_curl_global_state global_state);
117
118bool expected_statuscode(const char *reply, const char *statuscodes);
119char *string_statuscode(int major, int minor);
120
121void test_file(char *path);
122mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
123 int crit_days_till_exp);
124char *fmt_url(check_curl_working_state workingState);
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
new file mode 100644
index 00000000..f51b2ee9
--- /dev/null
+++ b/plugins/check_curl.d/config.h
@@ -0,0 +1,116 @@
1#pragma once
2
3#include "../../config.h"
4#include "../common.h"
5#include "../../lib/states.h"
6#include "../../lib/thresholds.h"
7#include <stddef.h>
8#include <string.h>
9#include <sys/socket.h>
10#include "curl/curl.h"
11#include "perfdata.h"
12#include "regex.h"
13
14enum {
15 MAX_RE_SIZE = 1024,
16 HTTP_PORT = 80,
17 HTTPS_PORT = 443,
18 MAX_PORT = 65535,
19 DEFAULT_MAX_REDIRS = 15
20};
21
22enum {
23 FOLLOW_HTTP_CURL = 0,
24 FOLLOW_LIBCURL = 1
25};
26
27enum {
28 STICKY_NONE = 0,
29 STICKY_HOST = 1,
30 STICKY_PORT = 2
31};
32
33#define HTTP_EXPECT "HTTP/"
34#define DEFAULT_BUFFER_SIZE 2048
35#define DEFAULT_SERVER_URL "/"
36
37typedef struct {
38 char *server_address;
39 char *server_url;
40 char *host_name;
41
42 char *http_method;
43
44 char *http_post_data;
45
46 unsigned short virtualPort;
47 unsigned short serverPort;
48
49 bool use_ssl;
50 bool no_body;
51} check_curl_working_state;
52
53check_curl_working_state check_curl_working_state_init();
54
55typedef struct {
56 bool automatic_decompression;
57 bool haproxy_protocol;
58 long socket_timeout;
59 sa_family_t sin_family;
60 int curl_http_version;
61 char **http_opt_headers;
62 size_t http_opt_headers_count;
63 int ssl_version;
64 char *client_cert;
65 char *client_privkey;
66 char *ca_cert;
67 bool verify_peer_and_host;
68 char user_agent[DEFAULT_BUFFER_SIZE];
69 char proxy_auth[MAX_INPUT_BUFFER];
70 char user_auth[MAX_INPUT_BUFFER];
71 char *http_content_type;
72 char *cookie_jar_file;
73} check_curl_static_curl_config;
74
75typedef struct {
76 check_curl_working_state initial_config;
77
78 check_curl_static_curl_config curl_config;
79 int max_depth;
80 int followmethod;
81 int followsticky;
82
83 int maximum_age;
84
85 // the original regex string from the command line
86 char regexp[MAX_RE_SIZE];
87
88 // the compiled regex for usage later
89 regex_t compiled_regex;
90
91 mp_state_enum state_regex;
92 bool invert_regex;
93 bool check_cert;
94 bool continue_after_check_cert;
95 int days_till_exp_warn;
96 int days_till_exp_crit;
97 mp_thresholds thlds;
98 mp_range page_length_limits;
99 bool page_length_limits_is_set;
100 struct {
101 char string[MAX_INPUT_BUFFER];
102 bool is_present;
103 } server_expect;
104 char string_expect[MAX_INPUT_BUFFER];
105 char header_expect[MAX_INPUT_BUFFER];
106 mp_state_enum on_redirect_result_state;
107 bool on_redirect_dependent;
108
109 bool show_extended_perfdata;
110 bool show_body;
111
112 bool output_format_is_set;
113 mp_output_format output_format;
114} check_curl_config;
115
116check_curl_config check_curl_config_init();
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 96575672..468ded31 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -33,6 +33,8 @@ const char *progname = "check_dbi";
33const char *copyright = "2011-2024"; 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"
@@ -53,55 +55,25 @@ const char *email = "devel@monitoring-plugins.org";
53 55
54#include <stdarg.h> 56#include <stdarg.h>
55 57
56typedef enum {
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
68typedef struct {
69 char *key;
70 char *value;
71} driver_option_t;
72
73static char *host = NULL;
74static int verbose = 0; 58static int verbose = 0;
75 59
76static char *warning_range = NULL; 60typedef struct {
77static char *critical_range = NULL; 61 int errorcode;
78static thresholds *dbi_thresholds = NULL; 62 check_dbi_config config;
79 63} check_dbi_config_wrapper;
80static char *expect = NULL;
81
82static regex_t expect_re;
83static char *expect_re_str = NULL;
84static int expect_re_cflags = 0;
85
86static np_dbi_metric_t metric = METRIC_QUERY_RESULT;
87static np_dbi_type_t type = TYPE_NUMERIC;
88
89static char *np_dbi_driver = NULL;
90static driver_option_t *np_dbi_options = NULL;
91static int np_dbi_options_num = 0;
92static char *np_dbi_database = NULL;
93static char *np_dbi_query = NULL;
94 64
95static int process_arguments(int, char **); 65static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
96static int validate_arguments(void); 66static check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper /*config_wrapper*/);
97void print_usage(void); 67void print_usage(void);
98static void print_help(void); 68static void print_help(void);
99 69
100static double timediff(struct timeval, struct timeval); 70static double timediff(struct timeval /*start*/, struct timeval /*end*/);
101 71
102static void np_dbi_print_error(dbi_conn, char *, ...); 72static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
103 73
104static int do_query(dbi_conn, const char **, double *, double *); 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*/);
105 77
106int main(int argc, char **argv) { 78int main(int argc, char **argv) {
107 int status = STATE_UNKNOWN; 79 int status = STATE_UNKNOWN;
@@ -119,8 +91,6 @@ int main(int argc, char **argv) {
119 const char *query_val_str = NULL; 91 const char *query_val_str = NULL;
120 double query_val = 0.0; 92 double query_val = 0.0;
121 93
122 int i;
123
124 setlocale(LC_ALL, ""); 94 setlocale(LC_ALL, "");
125 bindtextdomain(PACKAGE, LOCALEDIR); 95 bindtextdomain(PACKAGE, LOCALEDIR);
126 textdomain(PACKAGE); 96 textdomain(PACKAGE);
@@ -128,8 +98,13 @@ int main(int argc, char **argv) {
128 /* Parse extra opts if any */ 98 /* Parse extra opts if any */
129 argv = np_extra_opts(&argc, argv, progname); 99 argv = np_extra_opts(&argc, argv, progname);
130 100
131 if (process_arguments(argc, argv) == ERROR) 101 check_dbi_config_wrapper tmp = process_arguments(argc, argv);
102
103 if (tmp.errorcode == ERROR) {
132 usage4(_("Could not parse arguments")); 104 usage4(_("Could not parse arguments"));
105 }
106
107 const check_dbi_config config = tmp.config;
133 108
134 /* Set signal handling and alarm */ 109 /* Set signal handling and alarm */
135 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 110 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
@@ -137,13 +112,15 @@ int main(int argc, char **argv) {
137 } 112 }
138 alarm(timeout_interval); 113 alarm(timeout_interval);
139 114
140 if (verbose > 2) 115 if (verbose > 2) {
141 printf("Initializing DBI\n"); 116 printf("Initializing DBI\n");
117 }
142 118
143 dbi_inst *instance_p = {0}; 119 dbi_inst *instance_p = {0};
144 120
145 if (dbi_initialize_r(NULL, instance_p) < 0) { 121 if (dbi_initialize_r(NULL, instance_p) < 0) {
146 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");
147 return STATE_UNKNOWN; 124 return STATE_UNKNOWN;
148 } 125 }
149 126
@@ -152,15 +129,18 @@ int main(int argc, char **argv) {
152 return STATE_UNKNOWN; 129 return STATE_UNKNOWN;
153 } 130 }
154 131
155 if (verbose) 132 if (verbose) {
156 printf("Opening DBI driver '%s'\n", np_dbi_driver); 133 printf("Opening DBI driver '%s'\n", config.dbi_driver);
134 }
157 135
158 driver = dbi_driver_open_r(np_dbi_driver, instance_p); 136 driver = dbi_driver_open_r(config.dbi_driver, instance_p);
159 if (!driver) { 137 if (!driver) {
160 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", np_dbi_driver); 138 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
139 config.dbi_driver);
161 140
162 printf("Known drivers:\n"); 141 printf("Known drivers:\n");
163 for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { 142 for (driver = dbi_driver_list_r(NULL, instance_p); driver;
143 driver = dbi_driver_list_r(driver, instance_p)) {
164 printf(" - %s\n", dbi_driver_get_name(driver)); 144 printf(" - %s\n", dbi_driver_get_name(driver));
165 } 145 }
166 return STATE_UNKNOWN; 146 return STATE_UNKNOWN;
@@ -176,30 +156,36 @@ int main(int argc, char **argv) {
176 return STATE_UNKNOWN; 156 return STATE_UNKNOWN;
177 } 157 }
178 158
179 for (i = 0; i < np_dbi_options_num; ++i) { 159 for (size_t i = 0; i < config.dbi_options_num; ++i) {
180 const char *opt; 160 const char *opt;
181 161
182 if (verbose > 1) 162 if (verbose > 1) {
183 printf("Setting DBI driver option '%s' to '%s'\n", np_dbi_options[i].key, np_dbi_options[i].value); 163 printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key,
164 config.dbi_options[i].value);
165 }
184 166
185 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)) {
186 continue; 168 continue;
169 }
187 /* else: status != 0 */ 170 /* else: status != 0 */
188 171
189 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", np_dbi_options[i].key, np_dbi_options[i].value); 172 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'",
173 config.dbi_options[i].key, config.dbi_options[i].value);
190 printf("Known driver options:\n"); 174 printf("Known driver options:\n");
191 175
192 for (opt = dbi_conn_get_option_list(conn, NULL); opt; opt = dbi_conn_get_option_list(conn, opt)) { 176 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
177 opt = dbi_conn_get_option_list(conn, opt)) {
193 printf(" - %s\n", opt); 178 printf(" - %s\n", opt);
194 } 179 }
195 dbi_conn_close(conn); 180 dbi_conn_close(conn);
196 return STATE_UNKNOWN; 181 return STATE_UNKNOWN;
197 } 182 }
198 183
199 if (host) { 184 if (config.host) {
200 if (verbose > 1) 185 if (verbose > 1) {
201 printf("Setting DBI driver option 'host' to '%s'\n", host); 186 printf("Setting DBI driver option 'host' to '%s'\n", config.host);
202 dbi_conn_set_option(conn, "host", host); 187 }
188 dbi_conn_set_option(conn, "host", config.host);
203 } 189 }
204 190
205 if (verbose) { 191 if (verbose) {
@@ -209,10 +195,12 @@ int main(int argc, char **argv) {
209 dbname = dbi_conn_get_option(conn, "dbname"); 195 dbname = dbi_conn_get_option(conn, "dbname");
210 host = dbi_conn_get_option(conn, "host"); 196 host = dbi_conn_get_option(conn, "host");
211 197
212 if (!dbname) 198 if (!dbname) {
213 dbname = "<unspecified>"; 199 dbname = "<unspecified>";
214 if (!host) 200 }
201 if (!host) {
215 host = "<unspecified>"; 202 host = "<unspecified>";
203 }
216 204
217 printf("Connecting to database '%s' at host '%s'\n", dbname, host); 205 printf("Connecting to database '%s' at host '%s'\n", dbname, host);
218 } 206 }
@@ -226,109 +214,140 @@ int main(int argc, char **argv) {
226 conn_time = timediff(start_timeval, end_timeval); 214 conn_time = timediff(start_timeval, end_timeval);
227 215
228 server_version = dbi_conn_get_engine_version(conn); 216 server_version = dbi_conn_get_engine_version(conn);
229 if (verbose) 217 if (verbose) {
230 printf("Connected to server version %u\n", server_version); 218 printf("Connected to server version %u\n", server_version);
219 }
231 220
232 if (metric == METRIC_SERVER_VERSION) 221 if (config.metric == METRIC_SERVER_VERSION) {
233 status = get_status(server_version, dbi_thresholds); 222 status = get_status(server_version, config.dbi_thresholds);
223 }
234 224
235 if (verbose) 225 if (verbose) {
236 printf("Time elapsed: %f\n", conn_time); 226 printf("Time elapsed: %f\n", conn_time);
227 }
237 228
238 if (metric == METRIC_CONN_TIME) 229 if (config.metric == METRIC_CONN_TIME) {
239 status = get_status(conn_time, dbi_thresholds); 230 status = get_status(conn_time, config.dbi_thresholds);
231 }
240 232
241 /* select a database */ 233 /* select a database */
242 if (np_dbi_database) { 234 if (config.dbi_database) {
243 if (verbose > 1) 235 if (verbose > 1) {
244 printf("Selecting database '%s'\n", np_dbi_database); 236 printf("Selecting database '%s'\n", config.dbi_database);
237 }
245 238
246 if (dbi_conn_select_db(conn, np_dbi_database)) { 239 if (dbi_conn_select_db(conn, config.dbi_database)) {
247 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", np_dbi_database); 240 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'",
241 config.dbi_database);
248 return STATE_UNKNOWN; 242 return STATE_UNKNOWN;
249 } 243 }
250 } 244 }
251 245
252 if (np_dbi_query) { 246 if (config.dbi_query) {
253 /* execute query */ 247 /* execute query */
254 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,
255 if (status != STATE_OK) 249 config.dbi_query);
250 if (status != STATE_OK) {
256 /* do_query prints an error message in this case */ 251 /* do_query prints an error message in this case */
257 return status; 252 return status;
253 }
258 254
259 if (metric == METRIC_QUERY_RESULT) { 255 if (config.metric == METRIC_QUERY_RESULT) {
260 if (expect) { 256 if (config.expect) {
261 if ((!query_val_str) || strcmp(query_val_str, expect)) 257 if ((!query_val_str) || strcmp(query_val_str, config.expect)) {
262 status = STATE_CRITICAL; 258 status = STATE_CRITICAL;
263 else 259 } else {
264 status = STATE_OK; 260 status = STATE_OK;
265 } else if (expect_re_str) { 261 }
262 } else if (config.expect_re_str) {
266 int err; 263 int err;
267 264
265 regex_t expect_re = {};
268 err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); 266 err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0);
269 if (!err) 267 if (!err) {
270 status = STATE_OK; 268 status = STATE_OK;
271 else if (err == REG_NOMATCH) 269 } else if (err == REG_NOMATCH) {
272 status = STATE_CRITICAL; 270 status = STATE_CRITICAL;
273 else { 271 } else {
274 char errmsg[1024]; 272 char errmsg[1024];
275 regerror(err, &expect_re, errmsg, sizeof(errmsg)); 273 regerror(err, &expect_re, errmsg, sizeof(errmsg));
276 printf("ERROR - failed to execute regular expression: %s\n", errmsg); 274 printf("ERROR - failed to execute regular expression: %s\n", errmsg);
277 status = STATE_CRITICAL; 275 status = STATE_CRITICAL;
278 } 276 }
279 } else 277 } else {
280 status = get_status(query_val, dbi_thresholds); 278 status = get_status(query_val, config.dbi_thresholds);
281 } else if (metric == METRIC_QUERY_TIME) 279 }
282 status = get_status(query_time, dbi_thresholds); 280 } else if (config.metric == METRIC_QUERY_TIME) {
281 status = get_status(query_time, config.dbi_thresholds);
282 }
283 } 283 }
284 284
285 if (verbose) 285 if (verbose) {
286 printf("Closing connection\n"); 286 printf("Closing connection\n");
287 }
287 dbi_conn_close(conn); 288 dbi_conn_close(conn);
288 289
289 /* 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
290 * which should have been reported and handled (abort) before 291 * which should have been reported and handled (abort) before
291 * ... unless we expected a string to be returned */ 292 * ... unless we expected a string to be returned */
292 assert((metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || (type == TYPE_STRING)); 293 assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) ||
294 (config.type == TYPE_STRING));
293 295
294 assert((type != TYPE_STRING) || (expect || expect_re_str)); 296 assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str));
295 297
296 printf("%s - connection time: %fs", state_text(status), conn_time); 298 printf("%s - connection time: %fs", state_text(status), conn_time);
297 if (np_dbi_query) { 299 if (config.dbi_query) {
298 if (type == TYPE_STRING) { 300 if (config.type == TYPE_STRING) {
299 assert(expect || expect_re_str); 301 assert(config.expect || config.expect_re_str);
300 printf(", '%s' returned '%s' in %fs", np_dbi_query, query_val_str ? query_val_str : "<nothing>", query_time); 302 printf(", '%s' returned '%s' in %fs", config.dbi_query,
303 query_val_str ? query_val_str : "<nothing>", query_time);
301 if (status != STATE_OK) { 304 if (status != STATE_OK) {
302 if (expect) 305 if (config.expect) {
303 printf(" (expected '%s')", expect); 306 printf(" (expected '%s')", config.expect);
304 else if (expect_re_str) 307 } else if (config.expect_re_str) {
305 printf(" (expected regex /%s/%s)", expect_re_str, ((expect_re_cflags & REG_ICASE) ? "i" : "")); 308 printf(" (expected regex /%s/%s)", config.expect_re_str,
309 ((config.expect_re_cflags & REG_ICASE) ? "i" : ""));
310 }
306 } 311 }
307 } else if (isnan(query_val)) 312 } else if (isnan(query_val)) {
308 printf(", '%s' query execution time: %fs", np_dbi_query, query_time); 313 printf(", '%s' query execution time: %fs", config.dbi_query, query_time);
309 else 314 } else {
310 printf(", '%s' returned %f in %fs", np_dbi_query, query_val, query_time); 315 printf(", '%s' returned %f in %fs", config.dbi_query, query_val, query_time);
311 } 316 }
312 317 }
313 printf(" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, 318
314 ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "", 319 printf(
315 ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "", server_version, 320 " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
316 ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "", 321 ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "",
317 ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : ""); 322 ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "",
318 if (np_dbi_query) { 323 server_version,
319 if (!isnan(query_val)) /* this is also true when -e is used */ 324 ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range
320 printf(" query=%f;%s;%s;;", query_val, ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "", 325 : "",
321 ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : ""); 326 ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range
322 printf(" querytime=%fs;%s;%s;0;", query_time, ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "", 327 : "");
323 ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : ""); 328 if (config.dbi_query) {
329 if (!isnan(query_val)) { /* this is also true when -e is used */
330 printf(" query=%f;%s;%s;;", query_val,
331 ((config.metric == METRIC_QUERY_RESULT) && config.warning_range)
332 ? config.warning_range
333 : "",
334 ((config.metric == METRIC_QUERY_RESULT) && config.critical_range)
335 ? config.critical_range
336 : "");
337 }
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 : "");
324 } 344 }
325 printf("\n"); 345 printf("\n");
326 return status; 346 return status;
327} 347}
328 348
329/* process command-line arguments */ 349/* process command-line arguments */
330int process_arguments(int argc, char **argv) { 350check_dbi_config_wrapper process_arguments(int argc, char **argv) {
331 int c;
332 351
333 int option = 0; 352 int option = 0;
334 static struct option longopts[] = {STD_LONG_OPTS, 353 static struct option longopts[] = {STD_LONG_OPTS,
@@ -343,13 +362,19 @@ int process_arguments(int argc, char **argv) {
343 {"database", required_argument, 0, 'D'}, 362 {"database", required_argument, 0, 'D'},
344 {0, 0, 0, 0}}; 363 {0, 0, 0, 0}};
345 364
346 while (1) { 365 check_dbi_config_wrapper result = {
347 c = getopt_long(argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", longopts, &option); 366 .config = check_dbi_config_init(),
367 .errorcode = OK,
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);
348 372
349 if (c == EOF) 373 if (option_char == EOF) {
350 break; 374 break;
375 }
351 376
352 switch (c) { 377 switch (option_char) {
353 case '?': /* usage */ 378 case '?': /* usage */
354 usage5(); 379 usage5();
355 case 'h': /* help */ 380 case 'h': /* help */
@@ -360,135 +385,158 @@ int process_arguments(int argc, char **argv) {
360 exit(STATE_UNKNOWN); 385 exit(STATE_UNKNOWN);
361 386
362 case 'c': /* critical range */ 387 case 'c': /* critical range */
363 critical_range = optarg; 388 result.config.critical_range = optarg;
364 type = TYPE_NUMERIC; 389 result.config.type = TYPE_NUMERIC;
365 break; 390 break;
366 case 'w': /* warning range */ 391 case 'w': /* warning range */
367 warning_range = optarg; 392 result.config.warning_range = optarg;
368 type = TYPE_NUMERIC; 393 result.config.type = TYPE_NUMERIC;
369 break; 394 break;
370 case 'e': 395 case 'e':
371 expect = optarg; 396 result.config.expect = optarg;
372 type = TYPE_STRING; 397 result.config.type = TYPE_STRING;
373 break; 398 break;
374 case 'R': 399 case 'R':
375 expect_re_cflags = REG_ICASE; 400 result.config.expect_re_cflags = REG_ICASE;
376 /* fall through */ 401 /* fall through */
377 case 'r': { 402 case 'r': {
378 int err; 403 int err;
379 404
380 expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 405 result.config.expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
381 expect_re_str = optarg; 406 result.config.expect_re_str = optarg;
382 type = TYPE_STRING; 407 result.config.type = TYPE_STRING;
383 408
384 err = regcomp(&expect_re, expect_re_str, expect_re_cflags); 409 regex_t expect_re = {};
410 err = regcomp(&expect_re, result.config.expect_re_str, result.config.expect_re_cflags);
385 if (err) { 411 if (err) {
386 char errmsg[1024]; 412 char errmsg[1024];
387 regerror(err, &expect_re, errmsg, sizeof(errmsg)); 413 regerror(err, &expect_re, errmsg, sizeof(errmsg));
388 printf("ERROR - failed to compile regular expression: %s\n", errmsg); 414 printf("ERROR - failed to compile regular expression: %s\n", errmsg);
389 return ERROR; 415
416 result.errorcode = ERROR;
417 return result;
390 } 418 }
391 break; 419 break;
392 } 420 }
393 421
394 case 'm': 422 case 'm':
395 if (!strcasecmp(optarg, "CONN_TIME")) 423 if (!strcasecmp(optarg, "CONN_TIME")) {
396 metric = METRIC_CONN_TIME; 424 result.config.metric = METRIC_CONN_TIME;
397 else if (!strcasecmp(optarg, "SERVER_VERSION")) 425 } else if (!strcasecmp(optarg, "SERVER_VERSION")) {
398 metric = METRIC_SERVER_VERSION; 426 result.config.metric = METRIC_SERVER_VERSION;
399 else if (!strcasecmp(optarg, "QUERY_RESULT")) 427 } else if (!strcasecmp(optarg, "QUERY_RESULT")) {
400 metric = METRIC_QUERY_RESULT; 428 result.config.metric = METRIC_QUERY_RESULT;
401 else if (!strcasecmp(optarg, "QUERY_TIME")) 429 } else if (!strcasecmp(optarg, "QUERY_TIME")) {
402 metric = METRIC_QUERY_TIME; 430 result.config.metric = METRIC_QUERY_TIME;
403 else 431 } else {
404 usage2(_("Invalid metric"), optarg); 432 usage2(_("Invalid metric"), optarg);
433 }
405 break; 434 break;
406 case 't': /* timeout */ 435 case 't': /* timeout */
407 if (!is_intnonneg(optarg)) 436 if (!is_intnonneg(optarg)) {
408 usage2(_("Timeout interval must be a positive integer"), optarg); 437 usage2(_("Timeout interval must be a positive integer"), optarg);
409 else 438 } else {
410 timeout_interval = atoi(optarg); 439 timeout_interval = atoi(optarg);
440 }
411 441
412 break; 442 break;
413 case 'H': /* host */ 443 case 'H': /* host */
414 if (!is_host(optarg)) 444 if (!is_host(optarg)) {
415 usage2(_("Invalid hostname/address"), optarg); 445 usage2(_("Invalid hostname/address"), optarg);
416 else 446 } else {
417 host = optarg; 447 result.config.host = optarg;
448 }
418 break; 449 break;
419 case 'v': 450 case 'v':
420 verbose++; 451 verbose++;
421 break; 452 break;
422 453
423 case 'd': 454 case 'd':
424 np_dbi_driver = optarg; 455 result.config.dbi_driver = optarg;
425 break; 456 break;
426 case 'o': { 457 case 'o': {
427 driver_option_t *new; 458 driver_option_t *new = NULL;
428
429 char *k;
430 char *v;
431 459
432 k = optarg; 460 char *key = optarg;
433 v = strchr(k, (int)'='); 461 char *value = strchr(key, '=');
434 462
435 if (!v) 463 if (!value) {
436 usage2(_("Option must be '<key>=<value>'"), optarg); 464 usage2(_("Option must be '<key>=<value>'"), optarg);
465 }
437 466
438 *v = '\0'; 467 *value = '\0';
439 ++v; 468 ++value;
440 469
441 new = realloc(np_dbi_options, (np_dbi_options_num + 1) * sizeof(*new)); 470 new = realloc(result.config.dbi_options,
471 (result.config.dbi_options_num + 1) * sizeof(*new));
442 if (!new) { 472 if (!new) {
443 printf("UNKNOWN - failed to reallocate memory\n"); 473 printf("UNKNOWN - failed to reallocate memory\n");
444 exit(STATE_UNKNOWN); 474 exit(STATE_UNKNOWN);
445 } 475 }
446 476
447 np_dbi_options = new; 477 result.config.dbi_options = new;
448 new = np_dbi_options + np_dbi_options_num; 478 new = result.config.dbi_options + result.config.dbi_options_num;
449 ++np_dbi_options_num; 479 result.config.dbi_options_num++;
450 480
451 new->key = k; 481 new->key = key;
452 new->value = v; 482 new->value = value;
453 } break; 483 } break;
454 case 'q': 484 case 'q':
455 np_dbi_query = optarg; 485 result.config.dbi_query = optarg;
456 break; 486 break;
457 case 'D': 487 case 'D':
458 np_dbi_database = optarg; 488 result.config.dbi_database = optarg;
459 break; 489 break;
460 } 490 }
461 } 491 }
462 492
463 set_thresholds(&dbi_thresholds, warning_range, critical_range); 493 set_thresholds(&result.config.dbi_thresholds, result.config.warning_range,
494 result.config.critical_range);
464 495
465 return validate_arguments(); 496 return validate_arguments(result);
466} 497}
467 498
468int validate_arguments(void) { 499check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrapper) {
469 if (!np_dbi_driver) 500 if (!config_wrapper.config.dbi_driver) {
470 usage("Must specify a DBI driver"); 501 usage("Must specify a DBI driver");
502 }
471 503
472 if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME)) && (!np_dbi_query)) 504 if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) ||
505 (config_wrapper.config.metric == METRIC_QUERY_TIME)) &&
506 (!config_wrapper.config.dbi_query)) {
473 usage("Must specify a query to execute (metric == QUERY_RESULT)"); 507 usage("Must specify a query to execute (metric == QUERY_RESULT)");
508 }
474 509
475 if ((metric != METRIC_CONN_TIME) && (metric != METRIC_SERVER_VERSION) && (metric != METRIC_QUERY_RESULT) && 510 if ((config_wrapper.config.metric != METRIC_CONN_TIME) &&
476 (metric != METRIC_QUERY_TIME)) 511 (config_wrapper.config.metric != METRIC_SERVER_VERSION) &&
512 (config_wrapper.config.metric != METRIC_QUERY_RESULT) &&
513 (config_wrapper.config.metric != METRIC_QUERY_TIME)) {
477 usage("Invalid metric specified"); 514 usage("Invalid metric specified");
515 }
478 516
479 if (expect && (warning_range || critical_range || expect_re_str)) 517 if (config_wrapper.config.expect &&
518 (config_wrapper.config.warning_range || config_wrapper.config.critical_range ||
519 config_wrapper.config.expect_re_str)) {
480 usage("Do not mix -e and -w/-c/-r/-R"); 520 usage("Do not mix -e and -w/-c/-r/-R");
521 }
481 522
482 if (expect_re_str && (warning_range || critical_range || expect)) 523 if (config_wrapper.config.expect_re_str &&
524 (config_wrapper.config.warning_range || config_wrapper.config.critical_range ||
525 config_wrapper.config.expect)) {
483 usage("Do not mix -r/-R and -w/-c/-e"); 526 usage("Do not mix -r/-R and -w/-c/-e");
527 }
484 528
485 if (expect && (metric != METRIC_QUERY_RESULT)) 529 if (config_wrapper.config.expect && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) {
486 usage("Option -e requires metric QUERY_RESULT"); 530 usage("Option -e requires metric QUERY_RESULT");
531 }
487 532
488 if (expect_re_str && (metric != METRIC_QUERY_RESULT)) 533 if (config_wrapper.config.expect_re_str &&
534 (config_wrapper.config.metric != METRIC_QUERY_RESULT)) {
489 usage("Options -r/-R require metric QUERY_RESULT"); 535 usage("Options -r/-R require metric QUERY_RESULT");
536 }
490 537
491 return OK; 538 config_wrapper.errorcode = OK;
539 return config_wrapper;
492} 540}
493 541
494void print_help(void) { 542void print_help(void) {
@@ -518,6 +566,8 @@ void print_help(void) {
518 printf(" %s\n", _("DBI driver options")); 566 printf(" %s\n", _("DBI driver options"));
519 printf(" %s\n", "-q, --query=STRING"); 567 printf(" %s\n", "-q, --query=STRING");
520 printf(" %s\n", _("query to execute")); 568 printf(" %s\n", _("query to execute"));
569 printf(" %s\n", "-H STRING");
570 printf(" %s\n", _("target database host"));
521 printf("\n"); 571 printf("\n");
522 572
523 printf(UT_WARN_CRIT_RANGE); 573 printf(UT_WARN_CRIT_RANGE);
@@ -592,13 +642,8 @@ void print_usage(void) {
592 printf(" [-e <string>] [-r|-R <regex>]\n"); 642 printf(" [-e <string>] [-r|-R <regex>]\n");
593} 643}
594 644
595#define CHECK_IGNORE_ERROR(s) \ 645const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type,
596 do { \ 646 mp_dbi_metric metric, mp_dbi_type type) {
597 if (metric != METRIC_QUERY_RESULT) \
598 return (s); \
599 } while (0)
600
601const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type) {
602 const char *str; 647 const char *str;
603 648
604 if (field_type != DBI_TYPE_STRING) { 649 if (field_type != DBI_TYPE_STRING) {
@@ -608,17 +653,21 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty
608 653
609 str = dbi_result_get_string_idx(res, 1); 654 str = dbi_result_get_string_idx(res, 1);
610 if ((!str) || (strcmp(str, "ERROR") == 0)) { 655 if ((!str) || (strcmp(str, "ERROR") == 0)) {
611 CHECK_IGNORE_ERROR(NULL); 656 if (metric != METRIC_QUERY_RESULT) {
657 return NULL;
658 }
612 np_dbi_print_error(conn, "CRITICAL - failed to fetch string value"); 659 np_dbi_print_error(conn, "CRITICAL - failed to fetch string value");
613 return NULL; 660 return NULL;
614 } 661 }
615 662
616 if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) 663 if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) {
617 printf("Query returned string '%s'\n", str); 664 printf("Query returned string '%s'\n", str);
665 }
618 return str; 666 return str;
619} 667}
620 668
621double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type) { 669double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric,
670 mp_dbi_type type) {
622 double val = NAN; 671 double val = NAN;
623 672
624 if (*field_type == DBI_TYPE_INTEGER) { 673 if (*field_type == DBI_TYPE_INTEGER) {
@@ -629,26 +678,33 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type) {
629 const char *val_str; 678 const char *val_str;
630 char *endptr = NULL; 679 char *endptr = NULL;
631 680
632 val_str = get_field_str(conn, res, *field_type); 681 val_str = get_field_str(conn, res, *field_type, metric, type);
633 if (!val_str) { 682 if (!val_str) {
634 CHECK_IGNORE_ERROR(NAN); 683 if (metric != METRIC_QUERY_RESULT) {
684 return NAN;
685 }
635 *field_type = DBI_TYPE_ERROR; 686 *field_type = DBI_TYPE_ERROR;
636 return NAN; 687 return NAN;
637 } 688 }
638 689
639 val = strtod(val_str, &endptr); 690 val = strtod(val_str, &endptr);
640 if (endptr == val_str) { 691 if (endptr == val_str) {
641 CHECK_IGNORE_ERROR(NAN); 692 if (metric != METRIC_QUERY_RESULT) {
693 return NAN;
694 }
642 printf("CRITICAL - result value is not a numeric: %s\n", val_str); 695 printf("CRITICAL - result value is not a numeric: %s\n", val_str);
643 *field_type = DBI_TYPE_ERROR; 696 *field_type = DBI_TYPE_ERROR;
644 return NAN; 697 return NAN;
645 } 698 }
646 if ((endptr != NULL) && (*endptr != '\0')) { 699 if ((endptr != NULL) && (*endptr != '\0')) {
647 if (verbose) 700 if (verbose) {
648 printf("Garbage after value: %s\n", endptr); 701 printf("Garbage after value: %s\n", endptr);
702 }
649 } 703 }
650 } else { 704 } else {
651 CHECK_IGNORE_ERROR(NAN); 705 if (metric != METRIC_QUERY_RESULT) {
706 return NAN;
707 }
652 printf("CRITICAL - cannot parse value of type %s (%i)\n", 708 printf("CRITICAL - cannot parse value of type %s (%i)\n",
653 (*field_type == DBI_TYPE_BINARY) ? "BINARY" 709 (*field_type == DBI_TYPE_BINARY) ? "BINARY"
654 : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" 710 : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME"
@@ -660,53 +716,67 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type) {
660 return val; 716 return val;
661} 717}
662 718
663double get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val) { 719mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str,
720 double *res_val, mp_dbi_metric metric, mp_dbi_type type) {
664 unsigned short field_type; 721 unsigned short field_type;
665 double val = NAN; 722 double val = NAN;
666 723
667 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { 724 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
668 CHECK_IGNORE_ERROR(STATE_OK); 725 if (metric != METRIC_QUERY_RESULT) {
726 return STATE_OK;
727 }
669 np_dbi_print_error(conn, "CRITICAL - failed to fetch rows"); 728 np_dbi_print_error(conn, "CRITICAL - failed to fetch rows");
670 return STATE_CRITICAL; 729 return STATE_CRITICAL;
671 } 730 }
672 731
673 if (dbi_result_get_numrows(res) < 1) { 732 if (dbi_result_get_numrows(res) < 1) {
674 CHECK_IGNORE_ERROR(STATE_OK); 733 if (metric != METRIC_QUERY_RESULT) {
734 return STATE_OK;
735 }
675 printf("WARNING - no rows returned\n"); 736 printf("WARNING - no rows returned\n");
676 return STATE_WARNING; 737 return STATE_WARNING;
677 } 738 }
678 739
679 if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { 740 if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
680 CHECK_IGNORE_ERROR(STATE_OK); 741 if (metric != METRIC_QUERY_RESULT) {
742 return STATE_OK;
743 }
681 np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); 744 np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
682 return STATE_CRITICAL; 745 return STATE_CRITICAL;
683 } 746 }
684 747
685 if (dbi_result_get_numfields(res) < 1) { 748 if (dbi_result_get_numfields(res) < 1) {
686 CHECK_IGNORE_ERROR(STATE_OK); 749 if (metric != METRIC_QUERY_RESULT) {
750 return STATE_OK;
751 }
687 printf("WARNING - no fields returned\n"); 752 printf("WARNING - no fields returned\n");
688 return STATE_WARNING; 753 return STATE_WARNING;
689 } 754 }
690 755
691 if (dbi_result_first_row(res) != 1) { 756 if (dbi_result_first_row(res) != 1) {
692 CHECK_IGNORE_ERROR(STATE_OK); 757 if (metric != METRIC_QUERY_RESULT) {
758 return STATE_OK;
759 }
693 np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); 760 np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
694 return STATE_CRITICAL; 761 return STATE_CRITICAL;
695 } 762 }
696 763
697 field_type = dbi_result_get_field_type_idx(res, 1); 764 field_type = dbi_result_get_field_type_idx(res, 1);
698 if (field_type != DBI_TYPE_ERROR) { 765 if (field_type != DBI_TYPE_ERROR) {
699 if (type == TYPE_STRING) 766 if (type == TYPE_STRING) {
700 /* the value will be freed in dbi_result_free */ 767 /* the value will be freed in dbi_result_free */
701 *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));
702 else 769 } else {
703 val = get_field(conn, res, &field_type); 770 val = get_field(conn, res, &field_type, metric, type);
771 }
704 } 772 }
705 773
706 *res_val = val; 774 *res_val = val;
707 775
708 if (field_type == DBI_TYPE_ERROR) { 776 if (field_type == DBI_TYPE_ERROR) {
709 CHECK_IGNORE_ERROR(STATE_OK); 777 if (metric != METRIC_QUERY_RESULT) {
778 return STATE_OK;
779 }
710 np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); 780 np_dbi_print_error(conn, "CRITICAL - failed to fetch data");
711 return STATE_CRITICAL; 781 return STATE_CRITICAL;
712 } 782 }
@@ -715,19 +785,19 @@ double get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str,
715 return STATE_OK; 785 return STATE_OK;
716} 786}
717 787
718#undef CHECK_IGNORE_ERROR 788mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time,
719 789 mp_dbi_metric metric, mp_dbi_type type, char *np_dbi_query) {
720int do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time) {
721 dbi_result res; 790 dbi_result res;
722 791
723 struct timeval timeval_start; 792 struct timeval timeval_start;
724 struct timeval timeval_end; 793 struct timeval timeval_end;
725 int status = STATE_OK; 794 mp_state_enum status = STATE_OK;
726 795
727 assert(np_dbi_query); 796 assert(np_dbi_query);
728 797
729 if (verbose) 798 if (verbose) {
730 printf("Executing query '%s'\n", np_dbi_query); 799 printf("Executing query '%s'\n", np_dbi_query);
800 }
731 801
732 gettimeofday(&timeval_start, NULL); 802 gettimeofday(&timeval_start, NULL);
733 803
@@ -737,13 +807,14 @@ int do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *r
737 return STATE_CRITICAL; 807 return STATE_CRITICAL;
738 } 808 }
739 809
740 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);
741 811
742 gettimeofday(&timeval_end, NULL); 812 gettimeofday(&timeval_end, NULL);
743 *res_time = timediff(timeval_start, timeval_end); 813 *res_time = timediff(timeval_start, timeval_end);
744 814
745 if (verbose) 815 if (verbose) {
746 printf("Time elapsed: %f\n", *res_time); 816 printf("Time elapsed: %f\n", *res_time);
817 }
747 818
748 return status; 819 return status;
749} 820}
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 2bbd1e05..c27e5f13 100644
--- a/plugins/check_dig.c
+++ b/plugins/check_dig.c
@@ -41,97 +41,95 @@ const char *email = "devel@monitoring-plugins.org";
41#include "utils.h" 41#include "utils.h"
42#include "runcmd.h" 42#include "runcmd.h"
43 43
44static int process_arguments(int /*argc*/, char ** /*argv*/); 44#include "check_dig.d/config.h"
45static int validate_arguments(void); 45#include "states.h"
46
47typedef struct {
48 int errorcode;
49 check_dig_config config;
50} check_dig_config_wrapper;
51static check_dig_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
52static check_dig_config_wrapper validate_arguments(check_dig_config_wrapper /*config_wrapper*/);
53
46static void print_help(void); 54static void print_help(void);
47void print_usage(void); 55void print_usage(void);
48 56
49#define UNDEFINED 0 57static int verbose = 0;
50#define DEFAULT_PORT 53
51#define DEFAULT_TRIES 2
52
53static char *query_address = NULL;
54static char *record_type = "A";
55static char *expected_address = NULL;
56static char *dns_server = NULL;
57static char *dig_args = "";
58static char *query_transport = "";
59static bool verbose = false;
60static int server_port = DEFAULT_PORT;
61static int number_tries = DEFAULT_TRIES;
62static double warning_interval = UNDEFINED;
63static double critical_interval = UNDEFINED;
64static struct timeval tv;
65 58
66int main(int argc, char **argv) { 59int main(int argc, char **argv) {
67 char *command_line;
68 output chld_out;
69 output chld_err;
70 char *msg = NULL;
71 size_t i;
72 char *t;
73 long microsec;
74 double elapsed_time;
75 int result = STATE_UNKNOWN;
76 int timeout_interval_dig;
77
78 setlocale(LC_ALL, ""); 60 setlocale(LC_ALL, "");
79 bindtextdomain(PACKAGE, LOCALEDIR); 61 bindtextdomain(PACKAGE, LOCALEDIR);
80 textdomain(PACKAGE); 62 textdomain(PACKAGE);
81 63
82 /* Set signal handling and alarm */ 64 /* Set signal handling and alarm */
83 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) 65 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
84 usage_va(_("Cannot catch SIGALRM")); 66 usage_va(_("Cannot catch SIGALRM"));
67 }
85 68
86 /* Parse extra opts if any */ 69 /* Parse extra opts if any */
87 argv = np_extra_opts(&argc, argv, progname); 70 argv = np_extra_opts(&argc, argv, progname);
88 71
89 if (process_arguments(argc, argv) == ERROR) 72 check_dig_config_wrapper tmp_config = process_arguments(argc, argv);
73 if (tmp_config.errorcode == ERROR) {
90 usage_va(_("Could not parse arguments")); 74 usage_va(_("Could not parse arguments"));
75 }
76
77 const check_dig_config config = tmp_config.config;
91 78
92 /* dig applies the timeout to each try, so we need to work around this */ 79 /* dig applies the timeout to each try, so we need to work around this */
93 timeout_interval_dig = timeout_interval / number_tries + number_tries; 80 int timeout_interval_dig = ((int)timeout_interval / config.number_tries) + config.number_tries;
94 81
82 char *command_line;
95 /* get the command to run */ 83 /* get the command to run */
96 xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, dig_args, query_transport, server_port, dns_server, 84 xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG,
97 query_address, record_type, number_tries, timeout_interval_dig); 85 config.dig_args, config.query_transport, config.server_port, config.dns_server,
86 config.query_address, config.record_type, config.number_tries, timeout_interval_dig);
98 87
99 alarm(timeout_interval); 88 alarm(timeout_interval);
100 gettimeofday(&tv, NULL); 89 struct timeval start_time;
90 gettimeofday(&start_time, NULL);
101 91
102 if (verbose) { 92 if (verbose) {
103 printf("%s\n", command_line); 93 printf("%s\n", command_line);
104 if (expected_address != NULL) { 94 if (config.expected_address != NULL) {
105 printf(_("Looking for: '%s'\n"), expected_address); 95 printf(_("Looking for: '%s'\n"), config.expected_address);
106 } else { 96 } else {
107 printf(_("Looking for: '%s'\n"), query_address); 97 printf(_("Looking for: '%s'\n"), config.query_address);
108 } 98 }
109 } 99 }
110 100
101 output chld_out;
102 output chld_err;
103 char *msg = NULL;
104 mp_state_enum result = STATE_UNKNOWN;
111 /* run the command */ 105 /* run the command */
112 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { 106 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) {
113 result = STATE_WARNING; 107 result = STATE_WARNING;
114 msg = (char *)_("dig returned an error status"); 108 msg = (char *)_("dig returned an error status");
115 } 109 }
116 110
117 for (i = 0; i < chld_out.lines; i++) { 111 for (size_t i = 0; i < chld_out.lines; i++) {
118 /* the server is responding, we just got the host name... */ 112 /* the server is responding, we just got the host name... */
119 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { 113 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) {
120 114
121 /* loop through the whole 'ANSWER SECTION' */ 115 /* loop through the whole 'ANSWER SECTION' */
122 for (; i < chld_out.lines; i++) { 116 for (; i < chld_out.lines; i++) {
123 /* get the host address */ 117 /* get the host address */
124 if (verbose) 118 if (verbose) {
125 printf("%s\n", chld_out.line[i]); 119 printf("%s\n", chld_out.line[i]);
120 }
126 121
127 if (strcasestr(chld_out.line[i], (expected_address == NULL ? query_address : expected_address)) != NULL) { 122 if (strcasestr(chld_out.line[i], (config.expected_address == NULL
123 ? config.query_address
124 : config.expected_address)) != NULL) {
128 msg = chld_out.line[i]; 125 msg = chld_out.line[i];
129 result = STATE_OK; 126 result = STATE_OK;
130 127
131 /* Translate output TAB -> SPACE */ 128 /* Translate output TAB -> SPACE */
132 t = msg; 129 char *temp = msg;
133 while ((t = strchr(t, '\t')) != NULL) 130 while ((temp = strchr(temp, '\t')) != NULL) {
134 *t = ' '; 131 *temp = ' ';
132 }
135 break; 133 break;
136 } 134 }
137 } 135 }
@@ -154,37 +152,38 @@ int main(int argc, char **argv) {
154 /* If we get anything on STDERR, at least set warning */ 152 /* If we get anything on STDERR, at least set warning */
155 if (chld_err.buflen > 0) { 153 if (chld_err.buflen > 0) {
156 result = max_state(result, STATE_WARNING); 154 result = max_state(result, STATE_WARNING);
157 if (!msg) 155 if (!msg) {
158 for (i = 0; i < chld_err.lines; i++) { 156 for (size_t i = 0; i < chld_err.lines; i++) {
159 msg = strchr(chld_err.line[0], ':'); 157 msg = strchr(chld_err.line[0], ':');
160 if (msg) { 158 if (msg) {
161 msg++; 159 msg++;
162 break; 160 break;
163 } 161 }
164 } 162 }
163 }
165 } 164 }
166 165
167 microsec = deltime(tv); 166 long microsec = deltime(start_time);
168 elapsed_time = (double)microsec / 1.0e6; 167 double elapsed_time = (double)microsec / 1.0e6;
169 168
170 if (critical_interval > UNDEFINED && elapsed_time > critical_interval) 169 if (config.critical_interval > UNDEFINED && elapsed_time > config.critical_interval) {
171 result = STATE_CRITICAL; 170 result = STATE_CRITICAL;
171 }
172 172
173 else if (warning_interval > UNDEFINED && elapsed_time > warning_interval) 173 else if (config.warning_interval > UNDEFINED && elapsed_time > config.warning_interval) {
174 result = STATE_WARNING; 174 result = STATE_WARNING;
175 }
175 176
176 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, 177 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time,
177 msg ? msg : _("Probably a non-existent host/domain"), 178 msg ? msg : _("Probably a non-existent host/domain"),
178 fperfdata("time", elapsed_time, "s", (warning_interval > UNDEFINED ? true : false), warning_interval, 179 fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED),
179 (critical_interval > UNDEFINED ? true : false), critical_interval, true, 0, false, 0)); 180 config.warning_interval, (config.critical_interval > UNDEFINED),
180 return result; 181 config.critical_interval, true, 0, false, 0));
182 exit(result);
181} 183}
182 184
183/* process command-line arguments */ 185/* process command-line arguments */
184int process_arguments(int argc, char **argv) { 186check_dig_config_wrapper process_arguments(int argc, char **argv) {
185 int c;
186
187 int option = 0;
188 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 187 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
189 {"query_address", required_argument, 0, 'l'}, 188 {"query_address", required_argument, 0, 'l'},
190 {"warning", required_argument, 0, 'w'}, 189 {"warning", required_argument, 0, 'w'},
@@ -201,16 +200,25 @@ int process_arguments(int argc, char **argv) {
201 {"use-ipv6", no_argument, 0, '6'}, 200 {"use-ipv6", no_argument, 0, '6'},
202 {0, 0, 0, 0}}; 201 {0, 0, 0, 0}};
203 202
204 if (argc < 2) 203 check_dig_config_wrapper result = {
205 return ERROR; 204 .errorcode = OK,
205 .config = check_dig_config_init(),
206 };
206 207
207 while (1) { 208 if (argc < 2) {
208 c = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); 209 result.errorcode = ERROR;
210 return result;
211 }
209 212
210 if (c == -1 || c == EOF) 213 int option = 0;
214 while (true) {
215 int option_index = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option);
216
217 if (option_index == -1 || option_index == EOF) {
211 break; 218 break;
219 }
212 220
213 switch (c) { 221 switch (option_index) {
214 case 'h': /* help */ 222 case 'h': /* help */
215 print_help(); 223 print_help();
216 exit(STATE_UNKNOWN); 224 exit(STATE_UNKNOWN);
@@ -219,28 +227,28 @@ int process_arguments(int argc, char **argv) {
219 exit(STATE_UNKNOWN); 227 exit(STATE_UNKNOWN);
220 case 'H': /* hostname */ 228 case 'H': /* hostname */
221 host_or_die(optarg); 229 host_or_die(optarg);
222 dns_server = optarg; 230 result.config.dns_server = optarg;
223 break; 231 break;
224 case 'p': /* server port */ 232 case 'p': /* server port */
225 if (is_intpos(optarg)) { 233 if (is_intpos(optarg)) {
226 server_port = atoi(optarg); 234 result.config.server_port = atoi(optarg);
227 } else { 235 } else {
228 usage_va(_("Port must be a positive integer - %s"), optarg); 236 usage_va(_("Port must be a positive integer - %s"), optarg);
229 } 237 }
230 break; 238 break;
231 case 'l': /* address to lookup */ 239 case 'l': /* address to lookup */
232 query_address = optarg; 240 result.config.query_address = optarg;
233 break; 241 break;
234 case 'w': /* warning */ 242 case 'w': /* warning */
235 if (is_nonnegative(optarg)) { 243 if (is_nonnegative(optarg)) {
236 warning_interval = strtod(optarg, NULL); 244 result.config.warning_interval = strtod(optarg, NULL);
237 } else { 245 } else {
238 usage_va(_("Warning interval must be a positive integer - %s"), optarg); 246 usage_va(_("Warning interval must be a positive integer - %s"), optarg);
239 } 247 }
240 break; 248 break;
241 case 'c': /* critical */ 249 case 'c': /* critical */
242 if (is_nonnegative(optarg)) { 250 if (is_nonnegative(optarg)) {
243 critical_interval = strtod(optarg, NULL); 251 result.config.critical_interval = strtod(optarg, NULL);
244 } else { 252 } else {
245 usage_va(_("Critical interval must be a positive integer - %s"), optarg); 253 usage_va(_("Critical interval must be a positive integer - %s"), optarg);
246 } 254 }
@@ -253,48 +261,50 @@ int process_arguments(int argc, char **argv) {
253 } 261 }
254 break; 262 break;
255 case 'A': /* dig arguments */ 263 case 'A': /* dig arguments */
256 dig_args = strdup(optarg); 264 result.config.dig_args = strdup(optarg);
257 break; 265 break;
258 case 'v': /* verbose */ 266 case 'v': /* verbose */
259 verbose = true; 267 verbose++;
260 break; 268 break;
261 case 'T': 269 case 'T':
262 record_type = optarg; 270 result.config.record_type = optarg;
263 break; 271 break;
264 case 'a': 272 case 'a':
265 expected_address = optarg; 273 result.config.expected_address = optarg;
266 break; 274 break;
267 case '4': 275 case '4':
268 query_transport = "-4"; 276 result.config.query_transport = "-4";
269 break; 277 break;
270 case '6': 278 case '6':
271 query_transport = "-6"; 279 result.config.query_transport = "-6";
272 break; 280 break;
273 default: /* usage5 */ 281 default: /* usage5 */
274 usage5(); 282 usage5();
275 } 283 }
276 } 284 }
277 285
278 c = optind; 286 int index = optind;
279 if (dns_server == NULL) { 287 if (result.config.dns_server == NULL) {
280 if (c < argc) { 288 if (index < argc) {
281 host_or_die(argv[c]); 289 host_or_die(argv[index]);
282 dns_server = argv[c]; 290 result.config.dns_server = argv[index];
283 } else { 291 } else {
284 if (strcmp(query_transport, "-6") == 0) 292 if (strcmp(result.config.query_transport, "-6") == 0) {
285 dns_server = strdup("::1"); 293 result.config.dns_server = strdup("::1");
286 else 294 } else {
287 dns_server = strdup("127.0.0.1"); 295 result.config.dns_server = strdup("127.0.0.1");
296 }
288 } 297 }
289 } 298 }
290 299
291 return validate_arguments(); 300 return validate_arguments(result);
292} 301}
293 302
294int validate_arguments(void) { 303check_dig_config_wrapper validate_arguments(check_dig_config_wrapper config_wrapper) {
295 if (query_address != NULL) 304 if (config_wrapper.config.query_address == NULL) {
296 return OK; 305 config_wrapper.errorcode = ERROR;
297 return ERROR; 306 }
307 return config_wrapper;
298} 308}
299 309
300void print_help(void) { 310void print_help(void) {
@@ -328,7 +338,8 @@ void print_help(void) {
328 printf(" %s\n", "-T, --record_type=STRING"); 338 printf(" %s\n", "-T, --record_type=STRING");
329 printf(" %s\n", _("Record type to lookup (default: A)")); 339 printf(" %s\n", _("Record type to lookup (default: A)"));
330 printf(" %s\n", "-a, --expected_address=STRING"); 340 printf(" %s\n", "-a, --expected_address=STRING");
331 printf(" %s\n", _("An address expected to be in the answer section. If not set, uses whatever")); 341 printf(" %s\n",
342 _("An address expected to be in the answer section. If not set, uses whatever"));
332 printf(" %s\n", _("was in -l")); 343 printf(" %s\n", _("was in -l"));
333 printf(" %s\n", "-A, --dig-arguments=STRING"); 344 printf(" %s\n", "-A, --dig-arguments=STRING");
334 printf(" %s\n", _("Pass STRING as argument(s) to dig")); 345 printf(" %s\n", _("Pass STRING as argument(s) to dig"));
diff --git a/plugins/check_dig.d/config.h b/plugins/check_dig.d/config.h
new file mode 100644
index 00000000..a570b633
--- /dev/null
+++ b/plugins/check_dig.d/config.h
@@ -0,0 +1,40 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6#define UNDEFINED 0
7#define DEFAULT_PORT 53
8#define DEFAULT_TRIES 2
9
10typedef struct {
11 char *query_address;
12 char *record_type;
13 char *expected_address;
14 char *dns_server;
15 char *query_transport;
16 int server_port;
17 char *dig_args;
18 int number_tries;
19
20 double warning_interval;
21 double critical_interval;
22} check_dig_config;
23
24check_dig_config check_dig_config_init() {
25 check_dig_config tmp = {
26 .query_address = NULL,
27 .record_type = "A",
28 .expected_address = NULL,
29 .dns_server = NULL,
30 .query_transport = "",
31 .server_port = DEFAULT_PORT,
32 .dig_args = "",
33 .number_tries = DEFAULT_TRIES,
34
35 .warning_interval = UNDEFINED,
36 .critical_interval = UNDEFINED,
37
38 };
39 return tmp;
40}
diff --git a/plugins/check_disk.c b/plugins/check_disk.c
index 037a6f7a..d42b5486 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -31,24 +31,35 @@ const char *program_name = "check_disk"; /* Required for coreutils libs */
31const char *copyright = "1999-2024"; 31const char *copyright = "1999-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
34#include "states.h"
34#include "common.h" 35#include "common.h"
36#include "output.h"
37#include "perfdata.h"
38#include "utils_base.h"
39#include "lib/thresholds.h"
40
35#ifdef HAVE_SYS_STAT_H 41#ifdef HAVE_SYS_STAT_H
36# include <sys/stat.h> 42# include <sys/stat.h>
37#endif 43#endif
44
38#if HAVE_INTTYPES_H 45#if HAVE_INTTYPES_H
39# include <inttypes.h> 46# include <inttypes.h>
40#endif 47#endif
48
41#include <assert.h> 49#include <assert.h>
42#include "popen.h"
43#include "utils.h"
44#include "utils_disk.h"
45#include <stdarg.h> 50#include <stdarg.h>
46#include "fsusage.h" 51#include <stdint.h>
47#include "mountlist.h"
48#include <float.h> 52#include <float.h>
53#include "./popen.h"
54#include "./utils.h"
55#include "../gl/fsusage.h"
56#include "../gl/mountlist.h"
57#include "./check_disk.d/utils_disk.h"
58
49#if HAVE_LIMITS_H 59#if HAVE_LIMITS_H
50# include <limits.h> 60# include <limits.h>
51#endif 61#endif
62
52#include "regex.h" 63#include "regex.h"
53 64
54#ifdef __CYGWIN__ 65#ifdef __CYGWIN__
@@ -57,424 +68,322 @@ const char *email = "devel@monitoring-plugins.org";
57# define ERROR -1 68# define ERROR -1
58#endif 69#endif
59 70
60/* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62static int show_all_fs = 1;
63
64/* If nonzero, show only local filesystems. */
65static int show_local_fs = 0;
66
67/* If nonzero, show only local filesystems but call stat() on remote ones. */
68static int stat_remote_fs = 0;
69
70/* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72/* static int output_block_size; */
73
74/* If nonzero, invoke the `sync' system call before getting any usage data.
75 Using this option can make df very slow, especially with many or very
76 busy disks. Note that this may make a difference on some systems --
77 SunOs4.1.3, for one. It is *not* necessary on Linux. */
78/* static int require_sync = 0; */
79
80/* Linked list of filesystem types to display.
81 If `fs_select_list' is NULL, list all types.
82 This table is generated dynamically from command-line options,
83 rather than hardcoding into the program what it thinks are the
84 valid filesystem types; let the user specify any filesystem type
85 they want to, and if there are any filesystems of that type, they
86 will be shown.
87
88 Some filesystem types:
89 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
90
91/* static struct parameter_list *fs_select_list; */
92
93/* Linked list of filesystem types to omit.
94 If the list is empty, don't exclude any types. */
95static struct regex_list *fs_exclude_list = NULL;
96
97/* Linked list of filesystem types to check.
98 If the list is empty, include all types. */
99static struct regex_list *fs_include_list;
100
101static struct name_list *dp_exclude_list;
102
103static struct parameter_list *path_select_list = NULL;
104
105/* Linked list of mounted filesystems. */
106static struct mount_entry *mount_list;
107
108/* For long options that have no equivalent short option, use a
109 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
110enum {
111 SYNC_OPTION = CHAR_MAX + 1,
112 NO_SYNC_OPTION,
113 BLOCK_SIZE_OPTION
114};
115
116#ifdef _AIX 71#ifdef _AIX
117# pragma alloca 72# pragma alloca
118#endif 73#endif
119 74
120static int process_arguments(int /*argc*/, char ** /*argv*/); 75typedef struct {
121static void set_all_thresholds(struct parameter_list *path); 76 int errorcode;
122static void print_help(void); 77 check_disk_config config;
78} check_disk_config_wrapper;
79static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
80
81static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
82 char *crit_freespace_units, char *warn_freespace_percent,
83 char *crit_freespace_percent, char *warn_freeinodes_percent,
84 char *crit_freeinodes_percent);
85static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/);
86static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/);
87
88/*
89 * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control
90 * how reserved and inodes should be judged (ignored or not)
91 */
92static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp,
93 bool freespace_ignore_reserved);
94static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit,
95 bool display_inodes_perfdata, byte_unit unit);
96
123void print_usage(void); 97void print_usage(void);
124static double calculate_percent(uintmax_t, uintmax_t); 98static void print_help(void);
125static bool stat_path(struct parameter_list *p);
126static void get_stats(struct parameter_list *p, struct fs_usage *fsp);
127static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp);
128 99
129static char *units;
130static uintmax_t mult = 1024 * 1024;
131static int verbose = 0; 100static int verbose = 0;
132static bool erronly = false;
133static bool display_mntp = false;
134static bool exact_match = false;
135static bool ignore_missing = false;
136static bool freespace_ignore_reserved = false;
137static bool display_inodes_perfdata = false;
138static char *warn_freespace_units = NULL;
139static char *crit_freespace_units = NULL;
140static char *warn_freespace_percent = NULL;
141static char *crit_freespace_percent = NULL;
142static char *warn_usedspace_units = NULL;
143static char *crit_usedspace_units = NULL;
144static char *warn_usedspace_percent = NULL;
145static char *crit_usedspace_percent = NULL;
146static char *warn_usedinodes_percent = NULL;
147static char *crit_usedinodes_percent = NULL;
148static char *warn_freeinodes_percent = NULL;
149static char *crit_freeinodes_percent = NULL;
150static bool path_selected = false;
151static bool path_ignored = false;
152static char *group = NULL;
153static struct stat *stat_buf;
154static struct name_list *seen = NULL;
155 101
156int main(int argc, char **argv) { 102// This would not be necessary in C23!!
157 int result = STATE_UNKNOWN; 103const byte_unit Bytes_Factor = 1;
158 int disk_result = STATE_UNKNOWN; 104const byte_unit KibiBytes_factor = 1024;
159 char *output = NULL; 105const byte_unit MebiBytes_factor = 1048576;
160 char *ignored = NULL; 106const byte_unit GibiBytes_factor = 1073741824;
161 char *details = NULL; 107const byte_unit TebiBytes_factor = 1099511627776;
162 char *perf = NULL; 108const byte_unit PebiBytes_factor = 1125899906842624;
163 char *perf_ilabel = NULL; 109const byte_unit ExbiBytes_factor = 1152921504606846976;
164 char *preamble = " - free space:"; 110const byte_unit KiloBytes_factor = 1000;
165 char *ignored_preamble = " - ignored paths:"; 111const byte_unit MegaBytes_factor = 1000000;
166 char *flag_header = NULL; 112const byte_unit GigaBytes_factor = 1000000000;
167 int temp_result = STATE_UNKNOWN; 113const byte_unit TeraBytes_factor = 1000000000000;
168 114const byte_unit PetaBytes_factor = 1000000000000000;
169 struct mount_entry *me = NULL; 115const byte_unit ExaBytes_factor = 1000000000000000000;
170 struct fs_usage fsp = {0};
171 struct parameter_list *temp_list = NULL;
172 struct parameter_list *path = NULL;
173
174#ifdef __CYGWIN__
175 char mountdir[32];
176#endif
177
178 output = strdup("");
179 ignored = strdup("");
180 details = strdup("");
181 perf = strdup("");
182 perf_ilabel = strdup("");
183 stat_buf = malloc(sizeof *stat_buf);
184 116
117int main(int argc, char **argv) {
185 setlocale(LC_ALL, ""); 118 setlocale(LC_ALL, "");
186 bindtextdomain(PACKAGE, LOCALEDIR); 119 bindtextdomain(PACKAGE, LOCALEDIR);
187 textdomain(PACKAGE); 120 textdomain(PACKAGE);
188 121
189 mount_list = read_file_system_list(0); 122#ifdef __CYGWIN__
123 char mountdir[32];
124#endif
190 125
191 /* Parse extra opts if any */ 126 // Parse extra opts if any
192 argv = np_extra_opts(&argc, argv, progname); 127 argv = np_extra_opts(&argc, argv, progname);
193 128
194 if (process_arguments(argc, argv) == ERROR) 129 check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
130 if (tmp_config.errorcode == ERROR) {
195 usage4(_("Could not parse arguments")); 131 usage4(_("Could not parse arguments"));
132 }
196 133
197 /* If a list of paths has not been selected, find entire 134 check_disk_config config = tmp_config.config;
198 mount list and create list of paths 135
199 */ 136 if (config.output_format_is_set) {
200 if (path_selected == false && path_ignored == false) { 137 mp_set_format(config.output_format);
201 for (me = mount_list; me; me = me->me_next) {
202 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) {
203 path = np_add_parameter(&path_select_list, me->me_mountdir);
204 }
205 path->best_match = me;
206 path->group = group;
207 set_all_thresholds(path);
208 }
209 } 138 }
210 139
211 if (path_ignored == false) { 140 if (config.erronly) {
212 np_set_best_match(path_select_list, mount_list, exact_match); 141 mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
213 } 142 }
214 143
215 /* Error if no match found for specified paths */ 144 if (!config.path_ignored) {
216 temp_list = path_select_list; 145 mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list,
146 config.exact_match);
147 }
217 148
218 while (path_select_list) { 149 // Error if no match found for specified paths
219 if (!path_select_list->best_match && ignore_missing == true) { 150 for (parameter_list_elem *elem = config.path_select_list.first; elem;) {
220 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ 151 if (!elem->best_match && config.ignore_missing) {
221 if (path_select_list == temp_list) {
222 temp_list = path_select_list->name_next;
223 }
224 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
225 xasprintf(&ignored, "%s %s;", ignored, path_select_list->name);
226 /* Delete the path from the list so that it is not stat-checked later in the code. */ 152 /* Delete the path from the list so that it is not stat-checked later in the code. */
227 path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev); 153 elem = mp_int_fs_list_del(&config.path_select_list, elem);
228 } else if (!path_select_list->best_match) { 154 continue;
155 }
156 if (!elem->best_match) {
229 /* Without --ignore-missing option, exit with Critical state. */ 157 /* Without --ignore-missing option, exit with Critical state. */
230 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name); 158 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name);
231 } else {
232 /* Continue jumping through the list */
233 path_select_list = path_select_list->name_next;
234 } 159 }
235 }
236 160
237 path_select_list = temp_list; 161 elem = mp_int_fs_list_get_next(elem);
162 }
238 163
239 if (!path_select_list && ignore_missing == true) { 164 mp_check overall = mp_check_init();
240 result = STATE_OK; 165 if (config.path_select_list.length == 0) {
241 if (verbose >= 2) { 166 mp_subcheck none_sc = mp_subcheck_init();
242 printf("None of the provided paths were found\n"); 167 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
168 if (config.ignore_missing) {
169 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
170 } else {
171 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
172 if (verbose >= 2) {
173 printf("None of the provided paths were found\n");
174 }
243 } 175 }
176 mp_add_subcheck_to_check(&overall, none_sc);
177 mp_exit(overall);
244 } 178 }
245 179
246 /* Process for every path in list */ 180 // Filter list first
247 for (path = path_select_list; path; path = path->name_next) { 181 for (parameter_list_elem *path = config.path_select_list.first; path;) {
248 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) 182 if (!path->best_match) {
249 printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, 183 path = mp_int_fs_list_del(&config.path_select_list, path);
250 path->freespace_percent->critical->end);
251
252 if (verbose >= 3 && path->group != NULL)
253 printf("Group of %s: %s\n", path->name, path->group);
254
255 /* reset disk result */
256 disk_result = STATE_UNKNOWN;
257
258 me = path->best_match;
259
260 if (!me) {
261 continue; 184 continue;
262 } 185 }
263 186
187 struct mount_entry *mount_entry = path->best_match;
188
264#ifdef __CYGWIN__ 189#ifdef __CYGWIN__
265 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) 190 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) {
191 path = mp_int_fs_list_del(&config.path_select_list, path);
266 continue; 192 continue;
193 }
194
195 char *mountdir = NULL;
267 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); 196 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
268 if (GetDriveType(mountdir) != DRIVE_FIXED) 197 if (GetDriveType(mountdir) != DRIVE_FIXED) {
269 me->me_remote = 1; 198 mount_entry->me_remote = 1;
199 }
270#endif 200#endif
271 /* Filters */
272 201
273 /* Remove filesystems already seen */ 202 /* Remove filesystems already seen */
274 if (np_seen_name(seen, me->me_mountdir)) { 203 if (np_seen_name(config.seen, mount_entry->me_mountdir)) {
204 path = mp_int_fs_list_del(&config.path_select_list, path);
275 continue; 205 continue;
276 } 206 }
277 np_add_name(&seen, me->me_mountdir);
278 207
279 if (path->group == NULL) { 208 if (path->group == NULL) {
280 /* Skip remote filesystems if we're not interested in them */ 209 if (config.fs_exclude_list &&
281 if (me->me_remote && show_local_fs) { 210 np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) {
282 if (stat_remote_fs) { 211 // Skip excluded fs's
283 if (!stat_path(path) && ignore_missing == true) { 212 path = mp_int_fs_list_del(&config.path_select_list, path);
284 result = STATE_OK;
285 xasprintf(&ignored, "%s %s;", ignored, path->name);
286 }
287 }
288 continue; 213 continue;
289 /* Skip pseudo fs's if we haven't asked for all fs's */
290 } 214 }
291 if (me->me_dummy && !show_all_fs) { 215
292 continue; 216 if (config.device_path_exclude_list &&
293 /* Skip excluded fstypes */ 217 (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) ||
294 } 218 np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) {
295 if (fs_exclude_list && np_find_regmatch(fs_exclude_list, me->me_type)) { 219 // Skip excluded device or mount paths
220 path = mp_int_fs_list_del(&config.path_select_list, path);
296 continue; 221 continue;
297 /* Skip excluded fs's */
298 } 222 }
299 if (dp_exclude_list && (np_find_name(dp_exclude_list, me->me_devname) || np_find_name(dp_exclude_list, me->me_mountdir))) { 223
224 if (config.fs_include_list &&
225 !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) {
226 // Skip not included fstypes
227 path = mp_int_fs_list_del(&config.path_select_list, path);
300 continue; 228 continue;
301 /* Skip not included fstypes */
302 } 229 }
303 if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) { 230
231 /* Skip remote filesystems if we're not interested in them */
232 if (mount_entry->me_remote && config.show_local_fs) {
233 if (config.stat_remote_fs) {
234 // TODO Stat here
235 if (!stat_path(path, config.ignore_missing) && config.ignore_missing) {
236 }
237 }
304 continue; 238 continue;
305 } 239 }
306 }
307 240
308 if (!stat_path(path)) { 241 // TODO why stat here? remove unstatable fs?
309 if (ignore_missing == true) { 242 if (!stat_path(path, config.ignore_missing)) {
310 result = STATE_OK; 243 // if (config.ignore_missing) {
311 xasprintf(&ignored, "%s %s;", ignored, path->name); 244 // xasprintf(&ignored, "%s %s;", ignored, path->name);
245 // }
246 // not accessible, remove from list
247 path = mp_int_fs_list_del(&config.path_select_list, path);
248 continue;
312 } 249 }
313 continue;
314 } 250 }
315 get_fs_usage(me->me_mountdir, me->me_devname, &fsp);
316
317 if (fsp.fsu_blocks && strcmp("none", me->me_mountdir)) {
318 get_stats(path, &fsp);
319
320 if (verbose >= 3) {
321 printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f "
322 "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
323 me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units,
324 path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult);
325 }
326
327 /* Threshold comparisons */
328
329 temp_result = get_status(path->dfree_units, path->freespace_units);
330 if (verbose >= 3)
331 printf("Freespace_units result=%d\n", temp_result);
332 disk_result = max_state(disk_result, temp_result);
333
334 temp_result = get_status(path->dfree_pct, path->freespace_percent);
335 if (verbose >= 3)
336 printf("Freespace%% result=%d\n", temp_result);
337 disk_result = max_state(disk_result, temp_result);
338 251
339 temp_result = get_status(path->dused_units, path->usedspace_units); 252 path = mp_int_fs_list_get_next(path);
340 if (verbose >= 3) 253 }
341 printf("Usedspace_units result=%d\n", temp_result);
342 disk_result = max_state(disk_result, temp_result);
343
344 temp_result = get_status(path->dused_pct, path->usedspace_percent);
345 if (verbose >= 3)
346 printf("Usedspace_percent result=%d\n", temp_result);
347 disk_result = max_state(disk_result, temp_result);
348
349 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
350 if (verbose >= 3)
351 printf("Usedinodes_percent result=%d\n", temp_result);
352 disk_result = max_state(disk_result, temp_result);
353
354 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
355 if (verbose >= 3)
356 printf("Freeinodes_percent result=%d\n", temp_result);
357 disk_result = max_state(disk_result, temp_result);
358
359 result = max_state(result, disk_result);
360
361 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
362 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
363 data. Assumption that start=0. Roll on new syntax...
364 */
365
366 /* *_high_tide must be reinitialized at each run */
367 uint64_t warning_high_tide = UINT64_MAX;
368 254
369 if (path->freespace_units->warning != NULL) { 255 // now get the actual measurements
370 warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult; 256 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) {
371 } 257 // Get actual metrics here
372 if (path->freespace_percent->warning != NULL) { 258 struct mount_entry *mount_entry = filesystem->best_match;
373 warning_high_tide = 259 struct fs_usage fsp = {0};
374 min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * (path->dtotal_units * mult))); 260 get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp);
375 }
376 261
377 uint64_t critical_high_tide = UINT64_MAX; 262 if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
263 *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
378 264
379 if (path->freespace_units->critical != NULL) { 265 if (verbose >= 3) {
380 critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult; 266 printf("For %s, used_units=%lu free_units=%lu total_units=%lu "
381 } 267 "fsp.fsu_blocksize=%lu\n",
382 if (path->freespace_percent->critical != NULL) { 268 mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes,
383 critical_high_tide = 269 filesystem->total_bytes, fsp.fsu_blocksize);
384 min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * (path->dtotal_units * mult)));
385 } 270 }
271 } else {
272 // failed to retrieve file system data or not mounted?
273 filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem);
274 continue;
275 }
276 filesystem = mp_int_fs_list_get_next(filesystem);
277 }
386 278
387 /* Nb: *_high_tide are unset when == UINT64_MAX */ 279 if (verbose > 2) {
388 xasprintf(&perf, "%s %s", perf, 280 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
389 perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, 281 filesystem = mp_int_fs_list_get_next(filesystem)) {
390 path->dused_units * mult, "B", (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide, 282 assert(filesystem->best_match != NULL);
391 (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide, true, 0, true, 283 if (filesystem->best_match == NULL) {
392 path->dtotal_units * mult)); 284 printf("Filesystem path %s has no mount_entry!\n", filesystem->name);
393 285 } else {
394 if (display_inodes_perfdata) { 286 // printf("Filesystem path %s has a mount_entry!\n", filesystem->name);
395 /* *_high_tide must be reinitialized at each run */
396 warning_high_tide = UINT64_MAX;
397 critical_high_tide = UINT64_MAX;
398
399 if (path->freeinodes_percent->warning != NULL) {
400 warning_high_tide = (uint64_t)fabs(
401 min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total));
402 }
403 if (path->freeinodes_percent->critical != NULL) {
404 critical_high_tide = (uint64_t)fabs(min(
405 (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total));
406 }
407
408 xasprintf(&perf_ilabel, "%s (inodes)",
409 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
410 /* Nb: *_high_tide are unset when == UINT64_MAX */
411 xasprintf(&perf, "%s %s", perf,
412 perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX ? true : false),
413 warning_high_tide, (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide, true, 0,
414 true, path->inodes_total));
415 } 287 }
288 }
289 }
416 290
417 if (disk_result == STATE_OK && erronly && !verbose) 291 measurement_unit_list *measurements = NULL;
418 continue; 292 measurement_unit_list *current = NULL;
419 293 // create measuring units, because of groups
420 if (disk_result && verbose >= 1) { 294 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
421 xasprintf(&flag_header, " %s [", state_text(disk_result)); 295 filesystem = mp_int_fs_list_get_next(filesystem)) {
296 assert(filesystem->best_match != NULL);
297
298 if (filesystem->group == NULL) {
299 // create a measurement unit for the fs
300 measurement_unit unit =
301 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
302 if (measurements == NULL) {
303 measurements = current = add_measurement_list(NULL, unit);
422 } else { 304 } else {
423 xasprintf(&flag_header, ""); 305 current = add_measurement_list(measurements, unit);
424 } 306 }
425 xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, 307 } else {
426 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, path->dfree_units, units, 308 // Grouped elements are consecutive
427 path->dfree_pct); 309 if (measurements == NULL) {
428 if (path->dused_inodes_percent < 0) { 310 // first entry
429 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); 311 measurement_unit unit =
312 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
313 unit.name = strdup(filesystem->group);
314 measurements = current = add_measurement_list(NULL, unit);
430 } else { 315 } else {
431 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : "")); 316 // if this is the first element of a group, the name of the previous entry is
317 // different
318 if (strcmp(filesystem->group, current->unit.name) != 0) {
319 // so, this must be the first element of a group
320 measurement_unit unit =
321 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
322 unit.name = filesystem->group;
323 current = add_measurement_list(measurements, unit);
324
325 } else {
326 // NOT the first entry of a group, add info to the other one
327 current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem);
328 }
432 } 329 }
433 free(flag_header);
434 } 330 }
435 } 331 }
436 332
437 if (verbose >= 2) 333 /* Process for every path in list */
438 xasprintf(&output, "%s%s", output, details); 334 if (measurements != NULL) {
335 for (measurement_unit_list *unit = measurements; unit; unit = unit->next) {
336 mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata,
337 config.display_unit);
338 mp_add_subcheck_to_check(&overall, unit_sc);
339 }
340 } else {
341 // Apparently no machting fs found
342 mp_subcheck none_sc = mp_subcheck_init();
343 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
439 344
440 if (strcmp(output, "") == 0 && !erronly) { 345 if (config.ignore_missing) {
441 preamble = ""; 346 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
442 xasprintf(&output, " - No disks were found for provided parameters"); 347 } else {
348 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
349 }
350 mp_add_subcheck_to_check(&overall, none_sc);
443 } 351 }
444 352
445 printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, 353 mp_exit(overall);
446 (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf);
447 return result;
448} 354}
449 355
450double calculate_percent(uintmax_t value, uintmax_t total) { 356double calculate_percent(uintmax_t value, uintmax_t total) {
451 double pct = -1; 357 double pct = -1;
452 if (value <= DBL_MAX && total != 0) { 358 if (value <= DBL_MAX && total != 0) {
453 pct = (double)value / total * 100.0; 359 pct = (double)value / (double)total * 100.0;
454 } 360 }
361
455 return pct; 362 return pct;
456} 363}
457 364
458/* process command-line arguments */ 365/* process command-line arguments */
459int process_arguments(int argc, char **argv) { 366check_disk_config_wrapper process_arguments(int argc, char **argv) {
460 int c; 367
461 int err; 368 check_disk_config_wrapper result = {
462 struct parameter_list *se; 369 .errorcode = OK,
463 struct parameter_list *temp_list = NULL; 370 .config = check_disk_config_init(),
464 struct parameter_list *previous = NULL; 371 };
465 struct mount_entry *me; 372
466 regex_t re; 373 if (argc < 2) {
467 int cflags = REG_NOSUB | REG_EXTENDED; 374 result.errorcode = ERROR;
468 int default_cflags = cflags; 375 return result;
469 char errbuf[MAX_INPUT_BUFFER]; 376 }
470 int fnd = 0; 377
378 enum {
379 output_format_index = CHAR_MAX + 1,
380 display_unit_index,
381 };
471 382
472 int option = 0;
473 static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, 383 static struct option longopts[] = {{"timeout", required_argument, 0, 't'},
474 {"warning", required_argument, 0, 'w'}, 384 {"warning", required_argument, 0, 'w'},
475 {"critical", required_argument, 0, 'c'}, 385 {"critical", required_argument, 0, 'c'},
476 {"iwarning", required_argument, 0, 'W'}, 386 {"iwarning", required_argument, 0, 'W'},
477 /* Dang, -C is taken. We might want to reshuffle this. */
478 {"icritical", required_argument, 0, 'K'}, 387 {"icritical", required_argument, 0, 'K'},
479 {"kilobytes", no_argument, 0, 'k'}, 388 {"kilobytes", no_argument, 0, 'k'},
480 {"megabytes", no_argument, 0, 'm'}, 389 {"megabytes", no_argument, 0, 'm'},
@@ -507,24 +416,43 @@ int process_arguments(int argc, char **argv) {
507 {"clear", no_argument, 0, 'C'}, 416 {"clear", no_argument, 0, 'C'},
508 {"version", no_argument, 0, 'V'}, 417 {"version", no_argument, 0, 'V'},
509 {"help", no_argument, 0, 'h'}, 418 {"help", no_argument, 0, 'h'},
419 {"output-format", required_argument, 0, output_format_index},
420 {"display-unit", required_argument, 0, display_unit_index},
510 {0, 0, 0, 0}}; 421 {0, 0, 0, 0}};
511 422
512 if (argc < 2) 423 for (int index = 1; index < argc; index++) {
513 return ERROR; 424 if (strcmp("-to", argv[index]) == 0) {
425 strcpy(argv[index], "-t");
426 }
427 }
428
429 int cflags = REG_NOSUB | REG_EXTENDED;
430 int default_cflags = cflags;
431 char *warn_freespace_units = NULL;
432 char *crit_freespace_units = NULL;
433 char *warn_freespace_percent = NULL;
434 char *crit_freespace_percent = NULL;
435 char *warn_freeinodes_percent = NULL;
436 char *crit_freeinodes_percent = NULL;
514 437
515 np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); 438 bool path_selected = false;
439 char *group = NULL;
440 byte_unit unit = MebiBytes_factor;
516 441
517 for (c = 1; c < argc; c++) 442 result.config.mount_list = read_file_system_list(false);
518 if (strcmp("-to", argv[c]) == 0)
519 strcpy(argv[c], "-t");
520 443
521 while (1) { 444 np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
522 c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
523 445
524 if (c == -1 || c == EOF) 446 while (true) {
447 int option = 0;
448 int option_index = getopt_long(
449 argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
450
451 if (option_index == -1 || option_index == EOF) {
525 break; 452 break;
453 }
526 454
527 switch (c) { 455 switch (option_index) {
528 case 't': /* timeout period */ 456 case 't': /* timeout period */
529 if (is_integer(optarg)) { 457 if (is_integer(optarg)) {
530 timeout_interval = atoi(optarg); 458 timeout_interval = atoi(optarg);
@@ -555,10 +483,10 @@ int process_arguments(int argc, char **argv) {
555 break; 483 break;
556 484
557 /* Awful mistake where the range values do not make sense. Normally, 485 /* Awful mistake where the range values do not make sense. Normally,
558 you alert if the value is within the range, but since we are using 486 * you alert if the value is within the range, but since we are using
559 freespace, we have to alert if outside the range. Thus we artificially 487 * freespace, we have to alert if outside the range. Thus we artificially
560 force @ at the beginning of the range, so that it is backwards compatible 488 * force @ at the beginning of the range, so that it is backwards compatible
561 */ 489 */
562 case 'c': /* critical threshold */ 490 case 'c': /* critical threshold */
563 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 491 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
564 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); 492 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg);
@@ -594,181 +522,193 @@ int process_arguments(int argc, char **argv) {
594 } 522 }
595 break; 523 break;
596 case 'u': 524 case 'u':
597 if (units)
598 free(units);
599 if (!strcasecmp(optarg, "bytes")) { 525 if (!strcasecmp(optarg, "bytes")) {
600 mult = (uintmax_t)1; 526 unit = Bytes_Factor;
601 units = strdup("B");
602 } else if (!strcmp(optarg, "KiB")) { 527 } else if (!strcmp(optarg, "KiB")) {
603 mult = (uintmax_t)1024; 528 unit = KibiBytes_factor;
604 units = strdup("KiB");
605 } else if (!strcmp(optarg, "kB")) { 529 } else if (!strcmp(optarg, "kB")) {
606 mult = (uintmax_t)1000; 530 unit = KiloBytes_factor;
607 units = strdup("kB");
608 } else if (!strcmp(optarg, "MiB")) { 531 } else if (!strcmp(optarg, "MiB")) {
609 mult = (uintmax_t)1024 * 1024; 532 unit = MebiBytes_factor;
610 units = strdup("MiB");
611 } else if (!strcmp(optarg, "MB")) { 533 } else if (!strcmp(optarg, "MB")) {
612 mult = (uintmax_t)1000 * 1000; 534 unit = MegaBytes_factor;
613 units = strdup("MB");
614 } else if (!strcmp(optarg, "GiB")) { 535 } else if (!strcmp(optarg, "GiB")) {
615 mult = (uintmax_t)1024 * 1024 * 1024; 536 unit = GibiBytes_factor;
616 units = strdup("GiB");
617 } else if (!strcmp(optarg, "GB")) { 537 } else if (!strcmp(optarg, "GB")) {
618 mult = (uintmax_t)1000 * 1000 * 1000; 538 unit = GigaBytes_factor;
619 units = strdup("GB");
620 } else if (!strcmp(optarg, "TiB")) { 539 } else if (!strcmp(optarg, "TiB")) {
621 mult = (uintmax_t)1024 * 1024 * 1024 * 1024; 540 unit = TebiBytes_factor;
622 units = strdup("TiB");
623 } else if (!strcmp(optarg, "TB")) { 541 } else if (!strcmp(optarg, "TB")) {
624 mult = (uintmax_t)1000 * 1000 * 1000 * 1000; 542 unit = TeraBytes_factor;
625 units = strdup("TB");
626 } else if (!strcmp(optarg, "PiB")) { 543 } else if (!strcmp(optarg, "PiB")) {
627 mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; 544 unit = PebiBytes_factor;
628 units = strdup("PiB");
629 } else if (!strcmp(optarg, "PB")) { 545 } else if (!strcmp(optarg, "PB")) {
630 mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; 546 unit = PetaBytes_factor;
631 units = strdup("PB");
632 } else { 547 } else {
633 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); 548 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
634 } 549 }
635 if (units == NULL)
636 die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
637 break; 550 break;
638 case 'k': /* display mountpoint */ 551 case 'k':
639 mult = 1024; 552 unit = KibiBytes_factor;
640 if (units)
641 free(units);
642 units = strdup("kiB");
643 break; 553 break;
644 case 'm': /* display mountpoint */ 554 case 'm':
645 mult = 1024 * 1024; 555 unit = MebiBytes_factor;
646 if (units) 556 break;
647 free(units); 557 case display_unit_index:
648 units = strdup("MiB"); 558 if (!strcasecmp(optarg, "bytes")) {
559 result.config.display_unit = Bytes;
560 } else if (!strcmp(optarg, "KiB")) {
561 result.config.display_unit = KibiBytes;
562 } else if (!strcmp(optarg, "kB")) {
563 result.config.display_unit = KiloBytes;
564 } else if (!strcmp(optarg, "MiB")) {
565 result.config.display_unit = MebiBytes;
566 } else if (!strcmp(optarg, "MB")) {
567 result.config.display_unit = MegaBytes;
568 } else if (!strcmp(optarg, "GiB")) {
569 result.config.display_unit = GibiBytes;
570 } else if (!strcmp(optarg, "GB")) {
571 result.config.display_unit = GigaBytes;
572 } else if (!strcmp(optarg, "TiB")) {
573 result.config.display_unit = TebiBytes;
574 } else if (!strcmp(optarg, "TB")) {
575 result.config.display_unit = TeraBytes;
576 } else if (!strcmp(optarg, "PiB")) {
577 result.config.display_unit = PebiBytes;
578 } else if (!strcmp(optarg, "PB")) {
579 result.config.display_unit = PetaBytes;
580 } else {
581 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
582 }
649 break; 583 break;
650 case 'L': 584 case 'L':
651 stat_remote_fs = 1; 585 result.config.stat_remote_fs = true;
652 /* fallthrough */ 586 /* fallthrough */
653 case 'l': 587 case 'l':
654 show_local_fs = 1; 588 result.config.show_local_fs = true;
655 break; 589 break;
656 case 'P': 590 case 'P':
657 display_inodes_perfdata = 1; 591 result.config.display_inodes_perfdata = true;
658 break; 592 break;
659 case 'p': /* select path */ 593 case 'p': /* select path */ {
660 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 594 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
661 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 595 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
662 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { 596 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
663 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); 597 _("Must set a threshold value before using -p\n"));
664 } 598 }
665 599
666 /* add parameter if not found. overwrite thresholds if path has already been added */ 600 /* add parameter if not found. overwrite thresholds if path has already been added */
667 if (!(se = np_find_parameter(path_select_list, optarg))) { 601 parameter_list_elem *search_entry;
668 se = np_add_parameter(&path_select_list, optarg); 602 if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) {
669 603 search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg);
670 if (stat(optarg, &stat_buf[0]) && ignore_missing == true) { 604
671 path_ignored = true; 605 // struct stat stat_buf = {};
672 break; 606 // if (stat(optarg, &stat_buf) && result.config.ignore_missing) {
673 } 607 // result.config.path_ignored = true;
608 // break;
609 // }
674 } 610 }
675 se->group = group; 611 search_entry->group = group;
676 set_all_thresholds(se); 612 set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units,
613 warn_freespace_percent, crit_freespace_percent,
614
615 warn_freeinodes_percent, crit_freeinodes_percent);
677 616
678 /* With autofs, it is required to stat() the path before re-populating the mount_list */ 617 /* With autofs, it is required to stat() the path before re-populating the mount_list */
679 if (!stat_path(se)) { 618 // if (!stat_path(se, result.config.ignore_missing)) {
680 break; 619 // break;
681 } 620 // }
682 /* NB: We can't free the old mount_list "just like that": both list pointers and struct 621 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
683 * pointers are copied around. One of the reason it wasn't done yet is that other parts 622 result.config.exact_match);
684 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
685 mount_list = read_file_system_list(0);
686 np_set_best_match(se, mount_list, exact_match);
687 623
688 path_selected = true; 624 path_selected = true;
689 break; 625 } break;
690 case 'x': /* exclude path or partition */ 626 case 'x': /* exclude path or partition */
691 np_add_name(&dp_exclude_list, optarg); 627 np_add_name(&result.config.device_path_exclude_list, optarg);
692 break; 628 break;
693 case 'X': /* exclude file system type */ 629 case 'X': /* exclude file system type */ {
694 err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); 630 int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
695 if (err != 0) { 631 if (err != 0) {
696 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 632 char errbuf[MAX_INPUT_BUFFER];
697 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 633 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
634 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
635 _("Could not compile regular expression"), errbuf);
698 } 636 }
699 break; 637 break;
700 case 'N': /* include file system type */ 638 case 'N': /* include file system type */
701 err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); 639 err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
702 if (err != 0) { 640 if (err != 0) {
703 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 641 char errbuf[MAX_INPUT_BUFFER];
704 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 642 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
643 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
644 _("Could not compile regular expression"), errbuf);
705 } 645 }
706 break; 646 } break;
707 case 'v': /* verbose */ 647 case 'v': /* verbose */
708 verbose++; 648 verbose++;
709 break; 649 break;
710 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ 650 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
711 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ 651 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
712 erronly = true; 652 result.config.erronly = true;
713 break; 653 break;
714 case 'e': 654 case 'e':
715 erronly = true; 655 result.config.erronly = true;
716 break; 656 break;
717 case 'E': 657 case 'E':
718 if (path_selected) 658 if (path_selected) {
719 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); 659 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
720 exact_match = true; 660 _("Must set -E before selecting paths\n"));
661 }
662 result.config.exact_match = true;
721 break; 663 break;
722 case 'f': 664 case 'f':
723 freespace_ignore_reserved = true; 665 result.config.freespace_ignore_reserved = true;
724 break; 666 break;
725 case 'g': 667 case 'g':
726 if (path_selected) 668 if (path_selected) {
727 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); 669 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
670 _("Must set group value before selecting paths\n"));
671 }
728 group = optarg; 672 group = optarg;
729 break; 673 break;
730 case 'I': 674 case 'I':
731 cflags |= REG_ICASE; 675 cflags |= REG_ICASE;
732 // Intentional fallthrough 676 // Intentional fallthrough
733 case 'i': 677 case 'i': {
734 if (!path_selected) 678 if (!path_selected) {
735 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), 679 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"),
736 _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); 680 _("Paths need to be selected before using -i/-I. Use -A to select all paths "
737 err = regcomp(&re, optarg, cflags); 681 "explicitly"));
682 }
683 regex_t regex;
684 int err = regcomp(&regex, optarg, cflags);
738 if (err != 0) { 685 if (err != 0) {
739 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 686 char errbuf[MAX_INPUT_BUFFER];
740 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 687 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
688 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
689 _("Could not compile regular expression"), errbuf);
741 } 690 }
742 691
743 temp_list = path_select_list; 692 for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) {
693 if (elem->best_match) {
694 if (np_regex_match_mount_entry(elem->best_match, &regex)) {
744 695
745 previous = NULL; 696 if (verbose >= 3) {
746 while (temp_list) { 697 printf("ignoring %s matching regex\n", elem->name);
747 if (temp_list->best_match) { 698 }
748 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
749 699
750 if (verbose >= 3) 700 elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
751 printf("ignoring %s matching regex\n", temp_list->name); 701 continue;
752
753 temp_list = np_del_parameter(temp_list, previous);
754 /* pointer to first element needs to be updated if first item gets deleted */
755 if (previous == NULL)
756 path_select_list = temp_list;
757 } else {
758 previous = temp_list;
759 temp_list = temp_list->name_next;
760 } 702 }
761 } else {
762 previous = temp_list;
763 temp_list = temp_list->name_next;
764 } 703 }
704
705 elem = mp_int_fs_list_get_next(elem);
765 } 706 }
766 707
767 cflags = default_cflags; 708 cflags = default_cflags;
768 break; 709 } break;
769
770 case 'n': 710 case 'n':
771 ignore_missing = true; 711 result.config.ignore_missing = true;
772 break; 712 break;
773 case 'A': 713 case 'A':
774 optarg = strdup(".*"); 714 optarg = strdup(".*");
@@ -776,80 +716,96 @@ int process_arguments(int argc, char **argv) {
776 case 'R': 716 case 'R':
777 cflags |= REG_ICASE; 717 cflags |= REG_ICASE;
778 // Intentional fallthrough 718 // Intentional fallthrough
779 case 'r': 719 case 'r': {
780 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 720 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
781 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 721 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
782 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
783 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), 722 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
784 _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); 723 _("Must set a threshold value before using -r/-R/-A "
724 "(--ereg-path/--eregi-path/--all)\n"));
785 } 725 }
786 726
787 err = regcomp(&re, optarg, cflags); 727 regex_t regex;
728 int err = regcomp(&regex, optarg, cflags);
788 if (err != 0) { 729 if (err != 0) {
789 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 730 char errbuf[MAX_INPUT_BUFFER];
790 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 731 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
732 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
733 _("Could not compile regular expression"), errbuf);
791 } 734 }
792 735
793 for (me = mount_list; me; me = me->me_next) { 736 bool found = false;
794 if (np_regex_match_mount_entry(me, &re)) { 737 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
795 fnd = true; 738 if (np_regex_match_mount_entry(me, &regex)) {
796 if (verbose >= 3) 739 found = true;
797 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); 740 if (verbose >= 3) {
741 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir,
742 optarg);
743 }
798 744
799 /* add parameter if not found. overwrite thresholds if path has already been added */ 745 /* add parameter if not found. overwrite thresholds if path has already been
800 if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { 746 * added */
801 se = np_add_parameter(&path_select_list, me->me_mountdir); 747 parameter_list_elem *se = NULL;
748 if (!(se = mp_int_fs_list_find(result.config.path_select_list,
749 me->me_mountdir))) {
750 se =
751 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
802 } 752 }
803 se->group = group; 753 se->group = group;
804 set_all_thresholds(se); 754 set_all_thresholds(se, warn_freespace_units, crit_freespace_units,
755 warn_freespace_percent, crit_freespace_percent,
756 warn_freeinodes_percent, crit_freeinodes_percent);
805 } 757 }
806 } 758 }
807 759
808 if (!fnd && ignore_missing == true) { 760 if (!found) {
809 path_ignored = true; 761 if (result.config.ignore_missing) {
810 path_selected = true; 762 result.config.path_ignored = true;
811 break; 763 path_selected = true;
764 break;
765 }
766
767 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
768 _("Regular expression did not match any path or disk"), optarg);
812 } 769 }
813 if (!fnd)
814 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg);
815 770
816 fnd = false;
817 path_selected = true; 771 path_selected = true;
818 np_set_best_match(path_select_list, mount_list, exact_match); 772 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
773 result.config.exact_match);
819 cflags = default_cflags; 774 cflags = default_cflags;
820 775
821 break; 776 } break;
822 case 'M': /* display mountpoint */ 777 case 'M': /* display mountpoint */
823 display_mntp = true; 778 result.config.display_mntp = true;
824 break; 779 break;
825 case 'C': 780 case 'C': {
826 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ 781 /* add all mount entries to path_select list if no partitions have been explicitly
827 if (path_selected == false) { 782 * defined using -p */
828 struct parameter_list *path; 783 if (!path_selected) {
829 for (me = mount_list; me; me = me->me_next) { 784 parameter_list_elem *path;
830 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) 785 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
831 path = np_add_parameter(&path_select_list, me->me_mountdir); 786 if (!(path = mp_int_fs_list_find(result.config.path_select_list,
787 me->me_mountdir))) {
788 path =
789 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
790 }
832 path->best_match = me; 791 path->best_match = me;
833 path->group = group; 792 path->group = group;
834 set_all_thresholds(path); 793 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
794 warn_freespace_percent, crit_freespace_percent,
795 warn_freeinodes_percent, crit_freeinodes_percent);
835 } 796 }
836 } 797 }
798
837 warn_freespace_units = NULL; 799 warn_freespace_units = NULL;
838 crit_freespace_units = NULL; 800 crit_freespace_units = NULL;
839 warn_usedspace_units = NULL;
840 crit_usedspace_units = NULL;
841 warn_freespace_percent = NULL; 801 warn_freespace_percent = NULL;
842 crit_freespace_percent = NULL; 802 crit_freespace_percent = NULL;
843 warn_usedspace_percent = NULL;
844 crit_usedspace_percent = NULL;
845 warn_usedinodes_percent = NULL;
846 crit_usedinodes_percent = NULL;
847 warn_freeinodes_percent = NULL; 803 warn_freeinodes_percent = NULL;
848 crit_freeinodes_percent = NULL; 804 crit_freeinodes_percent = NULL;
849 805
850 path_selected = false; 806 path_selected = false;
851 group = NULL; 807 group = NULL;
852 break; 808 } break;
853 case 'V': /* version */ 809 case 'V': /* version */
854 print_revision(progname, NP_VERSION); 810 print_revision(progname, NP_VERSION);
855 exit(STATE_UNKNOWN); 811 exit(STATE_UNKNOWN);
@@ -858,50 +814,152 @@ int process_arguments(int argc, char **argv) {
858 exit(STATE_UNKNOWN); 814 exit(STATE_UNKNOWN);
859 case '?': /* help */ 815 case '?': /* help */
860 usage(_("Unknown argument")); 816 usage(_("Unknown argument"));
817 case output_format_index: {
818 parsed_output_format parser = mp_parse_output_format(optarg);
819 if (!parser.parsing_success) {
820 // TODO List all available formats here, maybe add anothoer usage function
821 printf("Invalid output format: %s\n", optarg);
822 exit(STATE_UNKNOWN);
823 }
824
825 result.config.output_format_is_set = true;
826 result.config.output_format = parser.output_format;
827 break;
828 }
861 } 829 }
862 } 830 }
863 831
864 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ 832 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
865 c = optind; 833 int index = optind;
866 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 834
867 warn_usedspace_percent = argv[c++]; 835 if (argc > index && is_intnonneg(argv[index])) {
836 if (verbose > 0) {
837 printf("Got an positional warn threshold: %s\n", argv[index]);
838 }
839 char *range = argv[index++];
840 mp_range_parsed tmp = mp_parse_range_string(range);
841 if (tmp.error != MP_PARSING_SUCCES) {
842 die(STATE_UNKNOWN, "failed to parse warning threshold");
843 }
844
845 mp_range tmp_range = tmp.range;
846 // Invert range to use it for free instead of used
847 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
868 848
869 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 849 warn_freespace_percent = mp_range_to_string(tmp_range);
870 crit_usedspace_percent = argv[c++];
871 850
872 if (argc > c) { 851 if (verbose > 0) {
873 se = np_add_parameter(&path_select_list, strdup(argv[c++])); 852 printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent);
853 }
854 }
855
856 if (argc > index && is_intnonneg(argv[index])) {
857 if (verbose > 0) {
858 printf("Got an positional crit threshold: %s\n", argv[index]);
859 }
860 char *range = argv[index++];
861 mp_range_parsed tmp = mp_parse_range_string(range);
862 if (tmp.error != MP_PARSING_SUCCES) {
863 die(STATE_UNKNOWN, "failed to parse warning threshold");
864 }
865
866 mp_range tmp_range = tmp.range;
867 // Invert range to use it for free instead of used
868 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
869
870 crit_freespace_percent = mp_range_to_string(tmp_range);
871
872 if (verbose > 0) {
873 printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent);
874 }
875 }
876
877 if (argc > index) {
878 if (verbose > 0) {
879 printf("Got an positional filesystem: %s\n", argv[index]);
880 }
881 struct parameter_list *se =
882 mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++]));
874 path_selected = true; 883 path_selected = true;
875 set_all_thresholds(se); 884 set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent,
885 crit_freespace_percent, warn_freeinodes_percent,
886 crit_freeinodes_percent);
876 } 887 }
877 888
878 if (units == NULL) { 889 // If a list of paths has not been explicitly selected, find entire
879 units = strdup("MiB"); 890 // mount list and create list of paths
880 mult = (uintmax_t)1024 * 1024; 891 if (!path_selected && !result.config.path_ignored) {
892 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
893 if (me->me_dummy != 0) {
894 // just do not add dummy filesystems
895 continue;
896 }
897
898 parameter_list_elem *path = NULL;
899 if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
900 path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
901 }
902 path->best_match = me;
903 path->group = group;
904 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
905 warn_freespace_percent, crit_freespace_percent,
906 warn_freeinodes_percent, crit_freeinodes_percent);
907 }
881 } 908 }
882 909
883 return true; 910 // Set thresholds to the appropriate unit
911 for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp;
912 tmp = mp_int_fs_list_get_next(tmp)) {
913
914 mp_perfdata_value factor = mp_create_pd_value(unit);
915
916 if (tmp->freespace_units.critical_is_set) {
917 tmp->freespace_units.critical =
918 mp_range_multiply(tmp->freespace_units.critical, factor);
919 }
920 if (tmp->freespace_units.warning_is_set) {
921 tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor);
922 }
923 }
924
925 return result;
884} 926}
885 927
886void set_all_thresholds(struct parameter_list *path) { 928void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
887 if (path->freespace_units != NULL) 929 char *crit_freespace_units, char *warn_freespace_percent,
888 free(path->freespace_units); 930 char *crit_freespace_percent, char *warn_freeinodes_percent,
889 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); 931 char *crit_freeinodes_percent) {
890 if (path->freespace_percent != NULL) 932 mp_range_parsed tmp;
891 free(path->freespace_percent); 933
892 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); 934 if (warn_freespace_units) {
893 if (path->usedspace_units != NULL) 935 tmp = mp_parse_range_string(warn_freespace_units);
894 free(path->usedspace_units); 936 path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
895 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); 937 }
896 if (path->usedspace_percent != NULL) 938
897 free(path->usedspace_percent); 939 if (crit_freespace_units) {
898 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); 940 tmp = mp_parse_range_string(crit_freespace_units);
899 if (path->usedinodes_percent != NULL) 941 path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
900 free(path->usedinodes_percent); 942 }
901 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); 943
902 if (path->freeinodes_percent != NULL) 944 if (warn_freespace_percent) {
903 free(path->freeinodes_percent); 945 tmp = mp_parse_range_string(warn_freespace_percent);
904 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); 946 path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range);
947 }
948
949 if (crit_freespace_percent) {
950 tmp = mp_parse_range_string(crit_freespace_percent);
951 path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range);
952 }
953
954 if (warn_freeinodes_percent) {
955 tmp = mp_parse_range_string(warn_freeinodes_percent);
956 path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range);
957 }
958
959 if (crit_freeinodes_percent) {
960 tmp = mp_parse_range_string(crit_freeinodes_percent);
961 path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range);
962 }
905} 963}
906 964
907void print_help(void) { 965void print_help(void) {
@@ -911,7 +969,8 @@ void print_help(void) {
911 printf(COPYRIGHT, copyright, email); 969 printf(COPYRIGHT, copyright, email);
912 970
913 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); 971 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
914 printf("%s\n", _("and generates an alert if free space is less than one of the threshold values")); 972 printf("%s\n",
973 _("and generates an alert if free space is less than one of the threshold values"));
915 974
916 printf("\n\n"); 975 printf("\n\n");
917 976
@@ -933,7 +992,8 @@ void print_help(void) {
933 printf(" %s\n", "-K, --icritical=PERCENT%"); 992 printf(" %s\n", "-K, --icritical=PERCENT%");
934 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); 993 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
935 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION"); 994 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION");
936 printf(" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); 995 printf(" %s\n",
996 _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
937 printf(" %s\n", "-x, --exclude_device=PATH <STRING>"); 997 printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
938 printf(" %s\n", _("Ignore device (only works if -p unspecified)")); 998 printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
939 printf(" %s\n", "-C, --clear"); 999 printf(" %s\n", "-C, --clear");
@@ -947,167 +1007,298 @@ void print_help(void) {
947 printf(" %s\n", "-P, --iperfdata"); 1007 printf(" %s\n", "-P, --iperfdata");
948 printf(" %s\n", _("Display inode usage in perfdata")); 1008 printf(" %s\n", _("Display inode usage in perfdata"));
949 printf(" %s\n", "-g, --group=NAME"); 1009 printf(" %s\n", "-g, --group=NAME");
950 printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); 1010 printf(" %s\n",
951 printf(" %s\n", "-k, --kilobytes"); 1011 _("Group paths. Thresholds apply to (free-)space of all partitions together"));
952 printf(" %s\n", _("Same as '--units kB'"));
953 printf(" %s\n", "-l, --local"); 1012 printf(" %s\n", "-l, --local");
954 printf(" %s\n", _("Only check local filesystems")); 1013 printf(" %s\n", _("Only check local filesystems"));
955 printf(" %s\n", "-L, --stat-remote-fs"); 1014 printf(" %s\n", "-L, --stat-remote-fs");
956 printf(" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); 1015 printf(
1016 " %s\n",
1017 _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
957 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); 1018 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
958 printf(" %s\n", "-M, --mountpoint"); 1019 printf(" %s\n", "-M, --mountpoint");
959 printf(" %s\n", _("Display the (block) device instead of the mount point")); 1020 printf(" %s\n", _("Display the (block) device instead of the mount point"));
960 printf(" %s\n", "-m, --megabytes");
961 printf(" %s\n", _("Same as '--units MB'"));
962 printf(" %s\n", "-A, --all"); 1021 printf(" %s\n", "-A, --all");
963 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); 1022 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
964 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); 1023 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
965 printf(" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)")); 1024 printf(" %s\n",
1025 _("Case insensitive regular expression for path/partition (may be repeated)"));
966 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); 1026 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
967 printf(" %s\n", _("Regular expression for path or partition (may be repeated)")); 1027 printf(" %s\n", _("Regular expression for path or partition (may be repeated)"));
968 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); 1028 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
969 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)")); 1029 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) "
1030 "(may be repeated)"));
970 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); 1031 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
971 printf(" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)")); 1032 printf(" %s\n",
1033 _("Regular expression to ignore selected path or partition (may be repeated)"));
972 printf(" %s\n", "-n, --ignore-missing"); 1034 printf(" %s\n", "-n, --ignore-missing");
973 printf(" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); 1035 printf(" %s\n",
1036 _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
974 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); 1037 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
975 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1038 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
976 printf(" %s\n", "-u, --units=STRING"); 1039 printf(" %s\n", "-u, --units=STRING");
977 printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); 1040 printf(" %s\n", _("Select the unit used for the absolute value thresholds"));
1041 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1042 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1043 printf(" %s\n", "-k, --kilobytes");
1044 printf(" %s\n", _("Same as '--units kB'"));
1045 printf(" %s\n", "--display-unit");
1046 printf(" %s\n", _("Select the unit used for in the output"));
1047 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1048 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1049 printf(" %s\n", "-m, --megabytes");
1050 printf(" %s\n", _("Same as '--units MB'"));
978 printf(UT_VERBOSE); 1051 printf(UT_VERBOSE);
979 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); 1052 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
980 printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); 1053 printf(" %s\n",
1054 _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
981 printf(" %s\n", "-N, --include-type=TYPE_REGEX"); 1055 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
982 printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); 1056 printf(
1057 " %s\n",
1058 _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1059 printf(UT_OUTPUT_FORMAT);
983 1060
984 printf("\n"); 1061 printf("\n");
985 printf("%s\n", _("General usage hints:")); 1062 printf("%s\n", _("General usage hints:"));
986 printf(" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); 1063 printf(
1064 " %s\n",
1065 _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
987 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\".")); 1066 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
988 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\"")); 1067 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} "
1068 "{thresholds b} ...\""));
989 1069
990 printf("\n"); 1070 printf("\n");
991 printf("%s\n", _("Examples:")); 1071 printf("%s\n", _("Examples:"));
992 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); 1072 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
993 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); 1073 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
994 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); 1074 printf(" %s\n",
995 printf(" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); 1075 "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
996 printf(" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together")); 1076 printf(
1077 " %s\n",
1078 _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
1079 printf(" %s\n\n",
1080 _("are grouped which means the freespace thresholds are applied to all disks together"));
997 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar"); 1081 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
998 printf(" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); 1082 printf(" %s\n",
1083 _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
999 1084
1000 printf(UT_SUPPORT); 1085 printf(UT_SUPPORT);
1001} 1086}
1002 1087
1003void print_usage(void) { 1088void print_usage(void) {
1004 printf("%s\n", _("Usage:")); 1089 printf("%s\n", _("Usage:"));
1005 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K " 1090 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c "
1091 "absolute_limit|-c percentage_limit%% | -K "
1006 "inode_percentage_limit } {-p path | -x device}\n", 1092 "inode_percentage_limit } {-p path | -x device}\n",
1007 progname); 1093 progname);
1008 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); 1094 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
1009 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); 1095 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1010} 1096}
1011 1097
1012bool stat_path(struct parameter_list *p) { 1098bool stat_path(parameter_list_elem *parameters, bool ignore_missing) {
1013 /* Stat entry to check that dir exists and is accessible */ 1099 /* Stat entry to check that dir exists and is accessible */
1014 if (verbose >= 3) 1100 if (verbose >= 3) {
1015 printf("calling stat on %s\n", p->name); 1101 printf("calling stat on %s\n", parameters->name);
1016 if (stat(p->name, &stat_buf[0])) { 1102 }
1017 if (verbose >= 3) 1103
1018 printf("stat failed on %s\n", p->name); 1104 struct stat stat_buf = {0};
1019 if (ignore_missing == true) { 1105 if (stat(parameters->name, &stat_buf)) {
1106 if (verbose >= 3) {
1107 printf("stat failed on %s\n", parameters->name);
1108 }
1109 if (ignore_missing) {
1020 return false; 1110 return false;
1021 } 1111 }
1022 printf("DISK %s - ", _("CRITICAL")); 1112 printf("DISK %s - ", _("CRITICAL"));
1023 die(STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); 1113 die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"),
1114 strerror(errno));
1024 } 1115 }
1116
1025 return true; 1117 return true;
1026} 1118}
1027 1119
1028void get_stats(struct parameter_list *p, struct fs_usage *fsp) { 1120static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp,
1029 struct parameter_list *p_list; 1121 bool freespace_ignore_reserved) {
1030 struct fs_usage tmpfsp; 1122 uintmax_t available = fsp.fsu_bavail;
1031 int first = 1; 1123 uintmax_t available_to_root = fsp.fsu_bfree;
1124 uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree;
1125 uintmax_t total;
1032 1126
1033 if (p->group == NULL) {
1034 get_path_stats(p, fsp);
1035 } else {
1036 /* find all group members */
1037 for (p_list = path_select_list; p_list; p_list = p_list->name_next) {
1038#ifdef __CYGWIN__
1039 if (strncmp(p_list->name, "/cygdrive/", 10) != 0)
1040 continue;
1041#endif
1042 if (p_list->group && !(strcmp(p_list->group, p->group))) {
1043 if (!stat_path(p_list))
1044 continue;
1045 get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
1046 get_path_stats(p_list, &tmpfsp);
1047 if (verbose >= 3)
1048 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n",
1049 p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units,
1050 p_list->dfree_units, p_list->dtotal_units, mult);
1051
1052 /* prevent counting the first FS of a group twice since its parameter_list entry
1053 * is used to carry the information of all file systems of the entire group */
1054 if (!first) {
1055 p->total += p_list->total;
1056 p->available += p_list->available;
1057 p->available_to_root += p_list->available_to_root;
1058 p->used += p_list->used;
1059
1060 p->dused_units += p_list->dused_units;
1061 p->dfree_units += p_list->dfree_units;
1062 p->dtotal_units += p_list->dtotal_units;
1063 p->inodes_total += p_list->inodes_total;
1064 p->inodes_free += p_list->inodes_free;
1065 p->inodes_free_to_root += p_list->inodes_free_to_root;
1066 p->inodes_used += p_list->inodes_used;
1067 }
1068 first = 0;
1069 }
1070 if (verbose >= 3)
1071 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group,
1072 p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult);
1073 }
1074 /* modify devname and mountdir for output */
1075 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1076 }
1077 /* finally calculate percentages for either plain FS or summed up group */
1078 p->dused_pct = calculate_percent(p->used, p->used + p->available); /* used + available can never be > uintmax */
1079 p->dfree_pct = 100.0 - p->dused_pct;
1080 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1081 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1082}
1083
1084void get_path_stats(struct parameter_list *p, struct fs_usage *fsp) {
1085 p->available = fsp->fsu_bavail;
1086 p->available_to_root = fsp->fsu_bfree;
1087 p->used = fsp->fsu_blocks - fsp->fsu_bfree;
1088 if (freespace_ignore_reserved) { 1127 if (freespace_ignore_reserved) {
1089 /* option activated : we subtract the root-reserved space from the total */ 1128 /* option activated : we subtract the root-reserved space from the total */
1090 p->total = fsp->fsu_blocks - p->available_to_root + p->available; 1129 total = fsp.fsu_blocks - available_to_root + available;
1091 } else { 1130 } else {
1092 /* default behaviour : take all the blocks into account */ 1131 /* default behaviour : take all the blocks into account */
1093 p->total = fsp->fsu_blocks; 1132 total = fsp.fsu_blocks;
1094 } 1133 }
1095 1134
1096 p->dused_units = p->used * fsp->fsu_blocksize / mult; 1135 parameters.used_bytes = used * fsp.fsu_blocksize;
1097 p->dfree_units = p->available * fsp->fsu_blocksize / mult; 1136 parameters.free_bytes = available * fsp.fsu_blocksize;
1098 p->dtotal_units = p->total * fsp->fsu_blocksize / mult; 1137 parameters.total_bytes = total * fsp.fsu_blocksize;
1138
1099 /* Free file nodes. Not sure the workaround is required, but in case...*/ 1139 /* Free file nodes. Not sure the workaround is required, but in case...*/
1100 p->inodes_free = fsp->fsu_ffree; 1140 parameters.inodes_free = fsp.fsu_ffree;
1101 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ 1141 parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */
1102 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; 1142 parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree;
1143
1103 if (freespace_ignore_reserved) { 1144 if (freespace_ignore_reserved) {
1104 /* option activated : we subtract the root-reserved inodes from the total */ 1145 /* option activated : we subtract the root-reserved inodes from the total */
1105 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ 1146 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1106 /* for others, fsp->fsu_ffree == fsp->fsu_favail */ 1147 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1107 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; 1148 parameters.inodes_total =
1149 fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free;
1108 } else { 1150 } else {
1109 /* default behaviour : take all the inodes into account */ 1151 /* default behaviour : take all the inodes into account */
1110 p->inodes_total = fsp->fsu_files; 1152 parameters.inodes_total = fsp.fsu_files;
1111 } 1153 }
1112 np_add_name(&seen, p->best_match->me_mountdir); 1154
1155 return parameters;
1156}
1157
1158mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata,
1159 byte_unit unit) {
1160 mp_subcheck result = mp_subcheck_init();
1161 result = mp_set_subcheck_default_state(result, STATE_UNKNOWN);
1162 xasprintf(&result.output, "%s", measurement_unit.name);
1163
1164 if (!measurement_unit.is_group && measurement_unit.filesystem_type) {
1165 xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type);
1166 }
1167
1168 /* Threshold comparisons */
1169
1170 // ===============================
1171 // Free space absolute values test
1172 mp_subcheck freespace_bytes_sc = mp_subcheck_init();
1173 freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK);
1174
1175 if (unit != Humanized) {
1176 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)",
1177 (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit),
1178 (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit));
1179 } else {
1180 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)",
1181 humanize_byte_value(measurement_unit.free_bytes, false),
1182 humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false));
1183 }
1184
1185 mp_perfdata used_space = perfdata_init();
1186 used_space.label = measurement_unit.name;
1187 used_space.value = mp_create_pd_value(measurement_unit.free_bytes);
1188 used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes));
1189 used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0));
1190 used_space.uom = "B";
1191 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1192 freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space));
1193
1194 // special case for absolute space thresholds here:
1195 // if absolute values are not set, compute the thresholds from percentage thresholds
1196 mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds;
1197 if (!temp_thlds.critical_is_set &&
1198 measurement_unit.freespace_percent_thresholds.critical_is_set) {
1199 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical;
1200
1201 if (!tmp_range.end_infinity) {
1202 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1203 measurement_unit.total_bytes);
1204 }
1205
1206 if (!tmp_range.start_infinity) {
1207 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1208 measurement_unit.total_bytes);
1209 }
1210 measurement_unit.freespace_bytes_thresholds =
1211 mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range);
1212 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1213 }
1214
1215 if (!temp_thlds.warning_is_set &&
1216 measurement_unit.freespace_percent_thresholds.warning_is_set) {
1217 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning;
1218 if (!tmp_range.end_infinity) {
1219 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1220 measurement_unit.total_bytes);
1221 }
1222 if (!tmp_range.start_infinity) {
1223 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1224 measurement_unit.total_bytes);
1225 }
1226 measurement_unit.freespace_bytes_thresholds =
1227 mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range);
1228 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1229 }
1230
1231 mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space);
1232 mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc);
1233
1234 // ==========================
1235 // Free space percentage test
1236 mp_subcheck freespace_percent_sc = mp_subcheck_init();
1237 freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK);
1238
1239 double free_percentage =
1240 calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes);
1241 xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage);
1242
1243 // Using perfdata here just to get to the test result
1244 mp_perfdata free_space_percent_pd = perfdata_init();
1245 free_space_percent_pd.value = mp_create_pd_value(free_percentage);
1246 free_space_percent_pd =
1247 mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds);
1248
1249 freespace_percent_sc =
1250 mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd));
1251 mp_add_subcheck_to_subcheck(&result, freespace_percent_sc);
1252
1253 // ================
1254 // Free inodes test
1255 // Only ever useful if the number of inodes is static (e.g. ext4),
1256 // not when it is dynamic (e.g btrfs)
1257 // Assumption: if the total number of inodes == 0, we have such a case and just skip the test
1258 if (measurement_unit.inodes_total > 0) {
1259 mp_subcheck freeindodes_percent_sc = mp_subcheck_init();
1260 freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK);
1261
1262 double free_inode_percentage =
1263 calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total);
1264
1265 if (verbose > 0) {
1266 printf("free inode percentage computed: %g\n", free_inode_percentage);
1267 }
1268
1269 xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)",
1270 free_inode_percentage, measurement_unit.inodes_free,
1271 measurement_unit.inodes_total);
1272
1273 mp_perfdata inodes_pd = perfdata_init();
1274 xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name);
1275 inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used);
1276 inodes_pd =
1277 mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total));
1278 inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0));
1279
1280 mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds;
1281
1282 if (absolut_inode_thresholds.critical_is_set) {
1283 absolut_inode_thresholds.critical =
1284 mp_range_multiply(absolut_inode_thresholds.critical,
1285 mp_create_pd_value(measurement_unit.inodes_total / 100));
1286 }
1287 if (absolut_inode_thresholds.warning_is_set) {
1288 absolut_inode_thresholds.warning =
1289 mp_range_multiply(absolut_inode_thresholds.warning,
1290 mp_create_pd_value(measurement_unit.inodes_total / 100));
1291 }
1292
1293 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds);
1294
1295 freeindodes_percent_sc =
1296 mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd));
1297 if (display_inodes_perfdata) {
1298 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd);
1299 }
1300 mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc);
1301 }
1302
1303 return result;
1113} 1304}
diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c
new file mode 100644
index 00000000..0b89018d
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.c
@@ -0,0 +1,528 @@
1/*****************************************************************************
2 *
3 * Library for check_disk
4 *
5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains utilities for check_disk. These are tested by libtap
11 *
12 *
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 *
27 *****************************************************************************/
28
29#include "../common.h"
30#include "utils_disk.h"
31#include "../../gl/fsusage.h"
32#include "../../lib/thresholds.h"
33#include "../../lib/states.h"
34#include <stdint.h>
35#include <stdio.h>
36#include <string.h>
37#include <assert.h>
38
39void np_add_name(struct name_list **list, const char *name) {
40 struct name_list *new_entry;
41 new_entry = (struct name_list *)malloc(sizeof *new_entry);
42 new_entry->name = (char *)name;
43 new_entry->next = *list;
44 *list = new_entry;
45}
46
47/* @brief Initialises a new regex at the begin of list via regcomp(3)
48 *
49 * @details if the regex fails to compile the error code of regcomp(3) is returned
50 * and list is not modified, otherwise list is modified to point to the new
51 * element
52 * @param list Pointer to a linked list of regex_list elements
53 * @param regex the string containing the regex which should be inserted into the list
54 * @param clags the cflags parameter for regcomp(3)
55 */
56int np_add_regex(struct regex_list **list, const char *regex, int cflags) {
57 struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry);
58
59 if (new_entry == NULL) {
60 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
61 }
62
63 int regcomp_result = regcomp(&new_entry->regex, regex, cflags);
64
65 if (!regcomp_result) {
66 // regcomp succeeded
67 new_entry->next = *list;
68 *list = new_entry;
69
70 return 0;
71 }
72 // regcomp failed
73 free(new_entry);
74
75 return regcomp_result;
76}
77
78parameter_list_elem parameter_list_init(const char *name) {
79 parameter_list_elem result = {
80 .name = strdup(name),
81 .best_match = NULL,
82
83 .freespace_units = mp_thresholds_init(),
84 .freespace_percent = mp_thresholds_init(),
85 .freeinodes_percent = mp_thresholds_init(),
86
87 .group = NULL,
88
89 .inodes_total = 0,
90 .inodes_free = 0,
91 .inodes_free_to_root = 0,
92 .inodes_used = 0,
93
94 .used_bytes = 0,
95 .free_bytes = 0,
96 .total_bytes = 0,
97
98 .next = NULL,
99 .prev = NULL,
100 };
101 return result;
102}
103
104/* Returns true if name is in list */
105bool np_find_name(struct name_list *list, const char *name) {
106 if (list == NULL || name == NULL) {
107 return false;
108 }
109 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
110 if (!strcmp(name, iterator->name)) {
111 return true;
112 }
113 }
114 return false;
115}
116
117/* Returns true if name is in list */
118bool np_find_regmatch(struct regex_list *list, const char *name) {
119 if (name == NULL) {
120 return false;
121 }
122
123 size_t len = strlen(name);
124
125 for (; list; list = list->next) {
126 /* Emulate a full match as if surrounded with ^( )$
127 by checking whether the match spans the whole name */
128 regmatch_t dummy_match;
129 if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 &&
130 dummy_match.rm_eo == len) {
131 return true;
132 }
133 }
134
135 return false;
136}
137
138bool np_seen_name(struct name_list *list, const char *name) {
139 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
140 if (!strcmp(iterator->name, name)) {
141 return true;
142 }
143 }
144 return false;
145}
146
147bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) {
148 return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) ||
149 (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0));
150}
151
152check_disk_config check_disk_config_init() {
153 check_disk_config tmp = {
154 .erronly = false,
155 .display_mntp = false,
156 .show_local_fs = false,
157 .stat_remote_fs = false,
158 .display_inodes_perfdata = false,
159
160 .exact_match = false,
161 .freespace_ignore_reserved = false,
162
163 .ignore_missing = false,
164 .path_ignored = false,
165
166 // FS Filters
167 .fs_exclude_list = NULL,
168 .fs_include_list = NULL,
169 .device_path_exclude_list = NULL,
170
171 // Actual filesystems paths to investigate
172 .path_select_list = filesystem_list_init(),
173
174 .mount_list = NULL,
175 .seen = NULL,
176
177 .display_unit = Humanized,
178 // .unit = MebiBytes,
179
180 .output_format_is_set = false,
181 };
182 return tmp;
183}
184
185char *get_unit_string(byte_unit_enum unit) {
186 switch (unit) {
187 case Bytes:
188 return "Bytes";
189 case KibiBytes:
190 return "KiB";
191 case MebiBytes:
192 return "MiB";
193 case GibiBytes:
194 return "GiB";
195 case TebiBytes:
196 return "TiB";
197 case PebiBytes:
198 return "PiB";
199 case ExbiBytes:
200 return "EiB";
201 case KiloBytes:
202 return "KB";
203 case MegaBytes:
204 return "MB";
205 case GigaBytes:
206 return "GB";
207 case TeraBytes:
208 return "TB";
209 case PetaBytes:
210 return "PB";
211 case ExaBytes:
212 return "EB";
213 default:
214 assert(false);
215 }
216}
217
218measurement_unit measurement_unit_init() {
219 measurement_unit tmp = {
220 .name = NULL,
221 .filesystem_type = NULL,
222 .is_group = false,
223
224 .freeinodes_percent_thresholds = mp_thresholds_init(),
225 .freespace_percent_thresholds = mp_thresholds_init(),
226 .freespace_bytes_thresholds = mp_thresholds_init(),
227
228 .free_bytes = 0,
229 .used_bytes = 0,
230 .total_bytes = 0,
231
232 .inodes_total = 0,
233 .inodes_free = 0,
234 .inodes_free_to_root = 0,
235 .inodes_used = 0,
236 };
237 return tmp;
238}
239
240// Add a given element to the list, memory for the new element is freshly allocated
241// Returns a pointer to new element
242measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem) {
243 // find last element
244 measurement_unit_list *new = NULL;
245 if (list == NULL) {
246 new = calloc(1, sizeof(measurement_unit_list));
247 if (new == NULL) {
248 die(STATE_UNKNOWN, _("allocation failed"));
249 }
250 } else {
251 measurement_unit_list *list_elem = list;
252 while (list_elem->next != NULL) {
253 list_elem = list_elem->next;
254 }
255
256 new = calloc(1, sizeof(measurement_unit_list));
257 if (new == NULL) {
258 die(STATE_UNKNOWN, _("allocation failed"));
259 }
260
261 list_elem->next = new;
262 }
263
264 new->unit = elem;
265 new->next = NULL;
266 return new;
267}
268
269measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit,
270 parameter_list_elem filesystem) {
271
272 unit.free_bytes += filesystem.free_bytes;
273 unit.used_bytes += filesystem.used_bytes;
274 unit.total_bytes += filesystem.total_bytes;
275
276 unit.inodes_total += filesystem.inodes_total;
277 unit.inodes_free += filesystem.inodes_free;
278 unit.inodes_free_to_root += filesystem.inodes_free_to_root;
279 unit.inodes_used += filesystem.inodes_used;
280 return unit;
281}
282
283measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem,
284 bool display_mntp) {
285 measurement_unit result = measurement_unit_init();
286 if (!display_mntp) {
287 result.name = strdup(filesystem.best_match->me_mountdir);
288 } else {
289 result.name = strdup(filesystem.best_match->me_devname);
290 }
291
292 if (filesystem.group) {
293 result.is_group = true;
294 } else {
295 result.is_group = false;
296 if (filesystem.best_match) {
297 result.filesystem_type = filesystem.best_match->me_type;
298 }
299 }
300
301 result.freeinodes_percent_thresholds = filesystem.freeinodes_percent;
302 result.freespace_percent_thresholds = filesystem.freespace_percent;
303 result.freespace_bytes_thresholds = filesystem.freespace_units;
304 result.free_bytes = filesystem.free_bytes;
305 result.total_bytes = filesystem.total_bytes;
306 result.used_bytes = filesystem.used_bytes;
307 result.inodes_total = filesystem.inodes_total;
308 result.inodes_used = filesystem.inodes_used;
309 result.inodes_free = filesystem.inodes_free;
310 result.inodes_free_to_root = filesystem.inodes_free_to_root;
311 return result;
312}
313
314#define RANDOM_STRING_LENGTH 64
315
316char *humanize_byte_value(unsigned long long value, bool use_si_units) {
317 // Idea: A reasonable output should have at most 3 orders of magnitude
318 // before the decimal separator
319 // 353GiB is ok, 2444 GiB should be 2.386 TiB
320 char *result = calloc(RANDOM_STRING_LENGTH, sizeof(char));
321 if (result == NULL) {
322 die(STATE_UNKNOWN, _("allocation failed"));
323 }
324 const byte_unit KibiBytes_factor = 1024;
325 const byte_unit MebiBytes_factor = 1048576;
326 const byte_unit GibiBytes_factor = 1073741824;
327 const byte_unit TebiBytes_factor = 1099511627776;
328 const byte_unit PebiBytes_factor = 1125899906842624;
329 const byte_unit ExbiBytes_factor = 1152921504606846976;
330 const byte_unit KiloBytes_factor = 1000;
331 const byte_unit MegaBytes_factor = 1000000;
332 const byte_unit GigaBytes_factor = 1000000000;
333 const byte_unit TeraBytes_factor = 1000000000000;
334 const byte_unit PetaBytes_factor = 1000000000000000;
335 const byte_unit ExaBytes_factor = 1000000000000000000;
336
337 if (use_si_units) {
338 // SI units, powers of 10
339 if (value < KiloBytes_factor) {
340 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
341 } else if (value < MegaBytes_factor) {
342 snprintf(result, RANDOM_STRING_LENGTH, "%llu KB", value / KiloBytes_factor);
343 } else if (value < GigaBytes_factor) {
344 snprintf(result, RANDOM_STRING_LENGTH, "%llu MB", value / MegaBytes_factor);
345 } else if (value < TeraBytes_factor) {
346 snprintf(result, RANDOM_STRING_LENGTH, "%llu GB", value / GigaBytes_factor);
347 } else if (value < PetaBytes_factor) {
348 snprintf(result, RANDOM_STRING_LENGTH, "%llu TB", value / TeraBytes_factor);
349 } else if (value < ExaBytes_factor) {
350 snprintf(result, RANDOM_STRING_LENGTH, "%llu PB", value / PetaBytes_factor);
351 } else {
352 snprintf(result, RANDOM_STRING_LENGTH, "%llu EB", value / ExaBytes_factor);
353 }
354 } else {
355 // IEC units, powers of 2 ^ 10
356 if (value < KibiBytes_factor) {
357 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
358 } else if (value < MebiBytes_factor) {
359 snprintf(result, RANDOM_STRING_LENGTH, "%llu KiB", value / KibiBytes_factor);
360 } else if (value < GibiBytes_factor) {
361 snprintf(result, RANDOM_STRING_LENGTH, "%llu MiB", value / MebiBytes_factor);
362 } else if (value < TebiBytes_factor) {
363 snprintf(result, RANDOM_STRING_LENGTH, "%llu GiB", value / GibiBytes_factor);
364 } else if (value < PebiBytes_factor) {
365 snprintf(result, RANDOM_STRING_LENGTH, "%llu TiB", value / TebiBytes_factor);
366 } else if (value < ExbiBytes_factor) {
367 snprintf(result, RANDOM_STRING_LENGTH, "%llu PiB", value / PebiBytes_factor);
368 } else {
369 snprintf(result, RANDOM_STRING_LENGTH, "%llu EiB", value / ExbiBytes_factor);
370 }
371 }
372
373 return result;
374}
375
376filesystem_list filesystem_list_init() {
377 filesystem_list tmp = {
378 .length = 0,
379 .first = NULL,
380 };
381 return tmp;
382}
383
384parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name) {
385 parameter_list_elem *current = list->first;
386 parameter_list_elem *new_path = (struct parameter_list *)malloc(sizeof *new_path);
387 *new_path = parameter_list_init(name);
388
389 if (current == NULL) {
390 list->first = new_path;
391 new_path->prev = NULL;
392 list->length = 1;
393 } else {
394 while (current->next) {
395 current = current->next;
396 }
397 current->next = new_path;
398 new_path->prev = current;
399 list->length++;
400 }
401 return new_path;
402}
403
404parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name) {
405 if (list.length == 0) {
406 return NULL;
407 }
408
409 for (parameter_list_elem *temp_list = list.first; temp_list; temp_list = temp_list->next) {
410 if (!strcmp(temp_list->name, name)) {
411 return temp_list;
412 }
413 }
414
415 return NULL;
416}
417
418parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item) {
419 if (list->length == 0) {
420 return NULL;
421 }
422
423 if (item == NULL) {
424 // Got NULL for item, interpret this as "delete first element"
425 // as a kind of compatibility to the old function
426 item = list->first;
427 }
428
429 if (list->first == item) {
430 list->length--;
431
432 list->first = item->next;
433 if (list->first) {
434 list->first->prev = NULL;
435 }
436 return list->first;
437 }
438
439 // Was not the first element, continue
440 parameter_list_elem *prev = list->first;
441 parameter_list_elem *current = list->first->next;
442
443 while (current != item && current != NULL) {
444 prev = current;
445 current = current->next;
446 }
447
448 if (current == NULL) {
449 // didn't find that element ....
450 return NULL;
451 }
452
453 // remove the element
454 parameter_list_elem *next = current->next;
455 prev->next = next;
456 list->length--;
457 if (next) {
458 next->prev = prev;
459 }
460
461 if (item->name) {
462 free(item->name);
463 }
464 free(item);
465
466 return next;
467}
468
469parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) {
470 if (!current) {
471 return NULL;
472 }
473 return current->next;
474}
475
476void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list,
477 bool exact) {
478 for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) {
479 if (!elem->best_match) {
480 size_t name_len = strlen(elem->name);
481 struct mount_entry *best_match = NULL;
482
483 /* set best match if path name exactly matches a mounted device name */
484 for (struct mount_entry *mount_entry = mount_list; mount_entry;
485 mount_entry = mount_entry->me_next) {
486 if (strcmp(mount_entry->me_devname, elem->name) == 0) {
487 struct fs_usage fsp;
488 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >=
489 0) {
490 best_match = mount_entry;
491 }
492 }
493 }
494
495 /* set best match by directory name if no match was found by devname */
496 if (!best_match) {
497 size_t best_match_len = 0;
498 for (struct mount_entry *mount_entry = mount_list; mount_entry;
499 mount_entry = mount_entry->me_next) {
500 size_t len = strlen(mount_entry->me_mountdir);
501
502 if ((!exact &&
503 (best_match_len <= len && len <= name_len &&
504 (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) ||
505 (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) {
506 struct fs_usage fsp;
507
508 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >=
509 0) {
510 best_match = mount_entry;
511 best_match_len = len;
512 }
513 }
514 }
515 }
516
517 if (best_match) {
518 elem->best_match = best_match;
519 } else {
520 elem->best_match =
521 NULL; /* Not sure why this is needed as it should be null on initialisation */
522 }
523
524 // No filesystem without a mount_entry!
525 // assert(elem->best_match != NULL);
526 }
527 }
528}
diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h
new file mode 100644
index 00000000..c96d4296
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.h
@@ -0,0 +1,160 @@
1#pragma once
2/* Header file for utils_disk */
3
4#include "../../config.h"
5#include "../../gl/mountlist.h"
6#include "../../lib/utils_base.h"
7#include "../../lib/output.h"
8#include "regex.h"
9#include <stdint.h>
10
11typedef unsigned long long byte_unit;
12
13typedef enum {
14 Humanized,
15 Bytes,
16 KibiBytes,
17 MebiBytes,
18 GibiBytes,
19 TebiBytes,
20 PebiBytes,
21 ExbiBytes,
22 KiloBytes,
23 MegaBytes,
24 GigaBytes,
25 TeraBytes,
26 PetaBytes,
27 ExaBytes,
28} byte_unit_enum;
29
30typedef struct name_list string_list;
31struct name_list {
32 char *name;
33 string_list *next;
34};
35
36struct regex_list {
37 regex_t regex;
38 struct regex_list *next;
39};
40
41typedef struct parameter_list parameter_list_elem;
42struct parameter_list {
43 char *name;
44 char *group;
45
46 mp_thresholds freespace_units;
47 mp_thresholds freespace_percent;
48 mp_thresholds freeinodes_percent;
49
50 struct mount_entry *best_match;
51
52 uintmax_t inodes_free_to_root;
53 uintmax_t inodes_free;
54 uintmax_t inodes_used;
55 uintmax_t inodes_total;
56
57 uint64_t used_bytes;
58 uint64_t free_bytes;
59 uint64_t total_bytes;
60
61 parameter_list_elem *next;
62 parameter_list_elem *prev;
63};
64
65typedef struct {
66 size_t length;
67 parameter_list_elem *first;
68} filesystem_list;
69
70filesystem_list filesystem_list_init();
71
72typedef struct {
73 char *name;
74 char *filesystem_type;
75 bool is_group;
76
77 mp_thresholds freespace_bytes_thresholds;
78 mp_thresholds freespace_percent_thresholds;
79 mp_thresholds freeinodes_percent_thresholds;
80
81 uintmax_t inodes_free_to_root;
82 uintmax_t inodes_free;
83 uintmax_t inodes_used;
84 uintmax_t inodes_total;
85
86 uintmax_t used_bytes;
87 uintmax_t free_bytes;
88 uintmax_t total_bytes;
89} measurement_unit;
90
91typedef struct measurement_unit_list measurement_unit_list;
92struct measurement_unit_list {
93 measurement_unit unit;
94 measurement_unit_list *next;
95};
96
97typedef struct {
98 // Output options
99 bool erronly;
100 bool display_mntp;
101 /* show only local filesystems. */
102 bool show_local_fs;
103 /* show only local filesystems but call stat() on remote ones. */
104 bool stat_remote_fs;
105 bool display_inodes_perfdata;
106
107 bool exact_match;
108 bool freespace_ignore_reserved;
109
110 bool ignore_missing;
111 bool path_ignored;
112
113 /* Linked list of filesystem types to omit.
114 If the list is empty, don't exclude any types. */
115 struct regex_list *fs_exclude_list;
116 /* Linked list of filesystem types to check.
117 If the list is empty, include all types. */
118 struct regex_list *fs_include_list;
119 struct name_list *device_path_exclude_list;
120 filesystem_list path_select_list;
121 /* Linked list of mounted filesystems. */
122 struct mount_entry *mount_list;
123 struct name_list *seen;
124
125 byte_unit_enum display_unit;
126 // byte_unit unit;
127
128 bool output_format_is_set;
129 mp_output_format output_format;
130} check_disk_config;
131
132void np_add_name(struct name_list **list, const char *name);
133bool np_find_name(struct name_list *list, const char *name);
134bool np_seen_name(struct name_list *list, const char *name);
135int np_add_regex(struct regex_list **list, const char *regex, int cflags);
136bool np_find_regmatch(struct regex_list *list, const char *name);
137
138parameter_list_elem parameter_list_init(const char *);
139
140parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name);
141parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name);
142parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item);
143parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current);
144void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list,
145 bool exact);
146
147measurement_unit measurement_unit_init();
148measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem);
149measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit,
150 parameter_list_elem filesystem);
151measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem,
152 bool display_mntp);
153
154int search_parameter_list(parameter_list_elem *list, const char *name);
155bool np_regex_match_mount_entry(struct mount_entry *, regex_t *);
156
157char *get_unit_string(byte_unit_enum);
158check_disk_config check_disk_config_init();
159
160char *humanize_byte_value(unsigned long long value, bool use_si_units);
diff --git a/plugins/check_dns.c b/plugins/check_dns.c
index e1e7c00e..56f91dae 100644
--- a/plugins/check_dns.c
+++ b/plugins/check_dns.c
@@ -39,26 +39,23 @@ 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
42static int process_arguments(int /*argc*/, char ** /*argv*/); 42#include "states.h"
43static int validate_arguments(void); 43#include "check_dns.d/config.h"
44static int error_scan(char * /*input_buffer*/, bool *); 44
45typedef struct {
46 int errorcode;
47 check_dns_config config;
48} check_dns_config_wrapper;
49static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/);
51static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/,
52 const char /*dns_server*/[ADDRESS_LENGTH]);
45static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/); 53static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/);
46static unsigned long ip2long(const char * /*src*/); 54static unsigned long ip2long(const char * /*src*/);
47static void print_help(void); 55static void print_help(void);
48void print_usage(void); 56void print_usage(void);
49 57
50#define ADDRESS_LENGTH 256
51static char query_address[ADDRESS_LENGTH] = "";
52static char dns_server[ADDRESS_LENGTH] = "";
53static char ptr_server[ADDRESS_LENGTH] = "";
54static bool verbose = false; 58static bool verbose = false;
55static char **expected_address = NULL;
56static int expected_address_cnt = 0;
57static bool expect_nxdomain = false;
58
59static bool expect_authority = false;
60static bool all_match = false;
61static thresholds *time_thresholds = NULL;
62 59
63static int qstrcmp(const void *p1, const void *p2) { 60static int qstrcmp(const void *p1, const void *p2) {
64 /* The actual arguments to this function are "pointers to 61 /* The actual arguments to this function are "pointers to
@@ -68,23 +65,6 @@ static int qstrcmp(const void *p1, const void *p2) {
68} 65}
69 66
70int main(int argc, char **argv) { 67int main(int argc, char **argv) {
71 char *command_line = NULL;
72 char input_buffer[MAX_INPUT_BUFFER];
73 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */
74 char **addresses = NULL;
75 int n_addresses = 0;
76 char *msg = NULL;
77 char *temp_buffer = NULL;
78 bool non_authoritative = false;
79 int result = STATE_UNKNOWN;
80 double elapsed_time;
81 long microsec;
82 struct timeval tv;
83 bool parse_address = false; /* This flag scans for Address: but only after Name: */
84 output chld_out;
85 output chld_err;
86 bool is_nxdomain = false;
87
88 setlocale(LC_ALL, ""); 68 setlocale(LC_ALL, "");
89 bindtextdomain(PACKAGE, LOCALEDIR); 69 bindtextdomain(PACKAGE, LOCALEDIR);
90 textdomain(PACKAGE); 70 textdomain(PACKAGE);
@@ -97,39 +77,66 @@ int main(int argc, char **argv) {
97 /* Parse extra opts if any */ 77 /* Parse extra opts if any */
98 argv = np_extra_opts(&argc, argv, progname); 78 argv = np_extra_opts(&argc, argv, progname);
99 79
100 if (process_arguments(argc, argv) == ERROR) { 80 check_dns_config_wrapper tmp = process_arguments(argc, argv);
81
82 if (tmp.errorcode == ERROR) {
101 usage_va(_("Could not parse arguments")); 83 usage_va(_("Could not parse arguments"));
102 } 84 }
103 85
86 const check_dns_config config = tmp.config;
87
88 char *command_line = NULL;
104 /* get the command to run */ 89 /* get the command to run */
105 xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, query_address, dns_server); 90 xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, config.query_address, config.dns_server);
106 91
92 struct timeval tv;
107 alarm(timeout_interval); 93 alarm(timeout_interval);
108 gettimeofday(&tv, NULL); 94 gettimeofday(&tv, NULL);
109 95
110 if (verbose) 96 if (verbose) {
111 printf("%s\n", command_line); 97 printf("%s\n", command_line);
98 }
112 99
100 output chld_out;
101 output chld_err;
102 char *msg = NULL;
103 mp_state_enum result = STATE_UNKNOWN;
113 /* run the command */ 104 /* run the command */
114 if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) { 105 if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) {
115 msg = (char *)_("nslookup returned an error status"); 106 msg = (char *)_("nslookup returned an error status");
116 result = STATE_WARNING; 107 result = STATE_WARNING;
117 } 108 }
118 109
119 /* scan stdout */ 110 /* =====
111 * scan stdout, main results get retrieved here
112 * =====
113 */
114 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */
115 char **addresses = NULL; // All addresses parsed from stdout
116 size_t n_addresses = 0; // counter for retrieved addresses
117 bool non_authoritative = false;
118 bool is_nxdomain = false;
119 bool parse_address = false; /* This flag scans for Address: but only after Name: */
120 for (size_t i = 0; i < chld_out.lines; i++) { 120 for (size_t i = 0; i < chld_out.lines; i++) {
121 if (addresses == NULL) 121 if (addresses == NULL) {
122 addresses = malloc(sizeof(*addresses) * 10); 122 addresses = malloc(sizeof(*addresses) * 10);
123 else if (!(n_addresses % 10)) 123 } else if (!(n_addresses % 10)) {
124 addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10)); 124 addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10));
125 }
125 126
126 if (verbose) 127 if (verbose) {
127 puts(chld_out.line[i]); 128 puts(chld_out.line[i]);
129 }
128 130
129 if (strcasestr(chld_out.line[i], ".in-addr.arpa") || strcasestr(chld_out.line[i], ".ip6.arpa")) { 131 if (strcasestr(chld_out.line[i], ".in-addr.arpa") ||
130 if ((temp_buffer = strstr(chld_out.line[i], "name = "))) 132 strcasestr(chld_out.line[i], ".ip6.arpa")) {
133 if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) {
134 continue;
135 }
136 char *temp_buffer = NULL;
137 if ((temp_buffer = strstr(chld_out.line[i], "name = "))) {
131 addresses[n_addresses++] = strdup(temp_buffer + 7); 138 addresses[n_addresses++] = strdup(temp_buffer + 7);
132 else { 139 } else {
133 msg = (char *)_("Warning plugin error"); 140 msg = (char *)_("Warning plugin error");
134 result = STATE_WARNING; 141 result = STATE_WARNING;
135 } 142 }
@@ -137,38 +144,54 @@ int main(int argc, char **argv) {
137 144
138 /* bug ID: 2946553 - Older versions of bind will use all available dns 145 /* bug ID: 2946553 - Older versions of bind will use all available dns
139 servers, we have to match the one specified */ 146 servers, we have to match the one specified */
140 if (strstr(chld_out.line[i], "Server:") && strlen(dns_server) > 0) { 147 if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) {
141 temp_buffer = strchr(chld_out.line[i], ':'); 148 char *temp_buffer = strchr(chld_out.line[i], ':');
149 if (temp_buffer == NULL) {
150 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"),
151 NSLOOKUP_COMMAND);
152 }
153
142 temp_buffer++; 154 temp_buffer++;
143 155
144 /* Strip leading tabs */ 156 /* Strip leading tabs */
145 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) 157 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) {
146 /* NOOP */; 158 /* NOOP */;
159 }
147 160
148 strip(temp_buffer); 161 strip(temp_buffer);
149 if (temp_buffer == NULL || strlen(temp_buffer) == 0) { 162 if (strlen(temp_buffer) == 0) {
150 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), NSLOOKUP_COMMAND); 163 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"),
164 NSLOOKUP_COMMAND);
151 } 165 }
152 166
153 if (strcmp(temp_buffer, dns_server) != 0) { 167 if (strcmp(temp_buffer, config.dns_server) != 0) {
154 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), dns_server); 168 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"),
169 config.dns_server);
155 } 170 }
156 } 171 }
157 172
158 /* the server is responding, we just got the host name... */ 173 /* the server is responding, we just got the host name... */
159 if (strstr(chld_out.line[i], "Name:")) 174 if (strstr(chld_out.line[i], "Name:")) {
160 parse_address = true; 175 parse_address = true;
161 else if (parse_address && (strstr(chld_out.line[i], "Address:") || strstr(chld_out.line[i], "Addresses:"))) { 176 } else if (parse_address && (strstr(chld_out.line[i], "Address:") ||
162 temp_buffer = index(chld_out.line[i], ':'); 177 strstr(chld_out.line[i], "Addresses:"))) {
178 char *temp_buffer = strchr(chld_out.line[i], ':');
179 if (temp_buffer == NULL) {
180 die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"),
181 NSLOOKUP_COMMAND);
182 }
183
163 temp_buffer++; 184 temp_buffer++;
164 185
165 /* Strip leading spaces */ 186 /* Strip leading spaces */
166 while (*temp_buffer == ' ') 187 while (*temp_buffer == ' ') {
167 temp_buffer++; 188 temp_buffer++;
189 }
168 190
169 strip(temp_buffer); 191 strip(temp_buffer);
170 if (temp_buffer == NULL || strlen(temp_buffer) == 0) { 192 if (strlen(temp_buffer) == 0) {
171 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), NSLOOKUP_COMMAND); 193 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"),
194 NSLOOKUP_COMMAND);
172 } 195 }
173 196
174 addresses[n_addresses++] = strdup(temp_buffer); 197 addresses[n_addresses++] = strdup(temp_buffer);
@@ -176,65 +199,74 @@ int main(int argc, char **argv) {
176 non_authoritative = true; 199 non_authoritative = true;
177 } 200 }
178 201
179 result = error_scan(chld_out.line[i], &is_nxdomain); 202 result = error_scan(chld_out.line[i], &is_nxdomain, config.dns_server);
180 if (result != STATE_OK) { 203 if (result != STATE_OK) {
181 msg = strchr(chld_out.line[i], ':'); 204 msg = strchr(chld_out.line[i], ':');
182 if (msg) 205 if (msg) {
183 msg++; 206 msg++;
207 }
184 break; 208 break;
185 } 209 }
186 } 210 }
187 211
212 char input_buffer[MAX_INPUT_BUFFER];
188 /* scan stderr */ 213 /* scan stderr */
189 for (size_t i = 0; i < chld_err.lines; i++) { 214 for (size_t i = 0; i < chld_err.lines; i++) {
190 if (verbose) 215 if (verbose) {
191 puts(chld_err.line[i]); 216 puts(chld_err.line[i]);
217 }
192 218
193 if (error_scan(chld_err.line[i], &is_nxdomain) != STATE_OK) { 219 if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) {
194 result = max_state(result, error_scan(chld_err.line[i], &is_nxdomain)); 220 result =
221 max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server));
195 msg = strchr(input_buffer, ':'); 222 msg = strchr(input_buffer, ':');
196 if (msg) 223 if (msg) {
197 msg++; 224 msg++;
198 else 225 } else {
199 msg = input_buffer; 226 msg = input_buffer;
227 }
200 } 228 }
201 } 229 }
202 230
203 if (is_nxdomain && !expect_nxdomain) { 231 if (is_nxdomain && !config.expect_nxdomain) {
204 die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address); 232 die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), config.query_address);
205 } 233 }
206 234
207 if (addresses) { 235 if (addresses) {
208 int i; 236 size_t slen = 1;
209 int slen; 237 char *adrp = NULL;
210 char *adrp;
211 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp); 238 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp);
212 for (i = 0, slen = 1; i < n_addresses; i++) { 239 for (size_t i = 0; i < n_addresses; i++) {
213 slen += strlen(addresses[i]) + 1; 240 slen += strlen(addresses[i]) + 1;
214 } 241 }
242
243 // Temporary pointer adrp gets moved, address stays on the beginning
215 adrp = address = malloc(slen); 244 adrp = address = malloc(slen);
216 for (i = 0; i < n_addresses; i++) { 245 for (size_t i = 0; i < n_addresses; i++) {
217 if (i) 246 if (i) {
218 *adrp++ = ','; 247 *adrp++ = ',';
248 }
219 strcpy(adrp, addresses[i]); 249 strcpy(adrp, addresses[i]);
220 adrp += strlen(addresses[i]); 250 adrp += strlen(addresses[i]);
221 } 251 }
222 *adrp = 0; 252 *adrp = 0;
223 } else 253 } else {
224 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), NSLOOKUP_COMMAND); 254 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"),
255 NSLOOKUP_COMMAND);
256 }
225 257
226 /* compare to expected address */ 258 /* compare to expected address */
227 if (result == STATE_OK && expected_address_cnt > 0) { 259 if (result == STATE_OK && config.expected_address_cnt > 0) {
228 result = STATE_CRITICAL; 260 result = STATE_CRITICAL;
229 temp_buffer = ""; 261 char *temp_buffer = "";
230 unsigned long expect_match = (1 << expected_address_cnt) - 1; 262 unsigned long expect_match = (1 << config.expected_address_cnt) - 1;
231 unsigned long addr_match = (1 << n_addresses) - 1; 263 unsigned long addr_match = (1 << n_addresses) - 1;
232 264
233 for (int i = 0; i < expected_address_cnt; i++) { 265 for (size_t i = 0; i < config.expected_address_cnt; i++) {
234 int j;
235 /* check if we get a match on 'raw' ip or cidr */ 266 /* check if we get a match on 'raw' ip or cidr */
236 for (j = 0; j < n_addresses; j++) { 267 for (size_t j = 0; j < n_addresses; j++) {
237 if (strcmp(addresses[j], expected_address[i]) == 0 || ip_match_cidr(addresses[j], expected_address[i])) { 268 if (strcmp(addresses[j], config.expected_address[i]) == 0 ||
269 ip_match_cidr(addresses[j], config.expected_address[i])) {
238 result = STATE_OK; 270 result = STATE_OK;
239 addr_match &= ~(1 << j); 271 addr_match &= ~(1 << j);
240 expect_match &= ~(1 << i); 272 expect_match &= ~(1 << i);
@@ -242,11 +274,12 @@ int main(int argc, char **argv) {
242 } 274 }
243 275
244 /* prepare an error string */ 276 /* prepare an error string */
245 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]); 277 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, config.expected_address[i]);
246 } 278 }
247 /* check if expected_address must cover all in addresses and none may be missing */ 279 /* check if expected_address must cover all in addresses and none may be missing */
248 if (all_match && (expect_match != 0 || addr_match != 0)) 280 if (config.all_match && (expect_match != 0 || addr_match != 0)) {
249 result = STATE_CRITICAL; 281 result = STATE_CRITICAL;
282 }
250 if (result == STATE_CRITICAL) { 283 if (result == STATE_CRITICAL) {
251 /* Strip off last semicolon... */ 284 /* Strip off last semicolon... */
252 temp_buffer[strlen(temp_buffer) - 2] = '\0'; 285 temp_buffer[strlen(temp_buffer) - 2] = '\0';
@@ -254,28 +287,31 @@ int main(int argc, char **argv) {
254 } 287 }
255 } 288 }
256 289
257 if (expect_nxdomain) { 290 if (config.expect_nxdomain) {
258 if (!is_nxdomain) { 291 if (!is_nxdomain) {
259 result = STATE_CRITICAL; 292 result = STATE_CRITICAL;
260 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address); 293 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address,
294 address);
261 } else { 295 } else {
262 if (address != NULL) 296 if (address != NULL) {
263 free(address); 297 free(address);
298 }
264 address = "NXDOMAIN"; 299 address = "NXDOMAIN";
265 } 300 }
266 } 301 }
267 302
268 /* check if authoritative */ 303 /* check if authoritative */
269 if (result == STATE_OK && expect_authority && non_authoritative) { 304 if (result == STATE_OK && config.expect_authority && non_authoritative) {
270 result = STATE_CRITICAL; 305 result = STATE_CRITICAL;
271 xasprintf(&msg, _("server %s is not authoritative for %s"), dns_server, query_address); 306 xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server,
307 config.query_address);
272 } 308 }
273 309
274 microsec = deltime(tv); 310 long microsec = deltime(tv);
275 elapsed_time = (double)microsec / 1.0e6; 311 double elapsed_time = (double)microsec / 1.0e6;
276 312
277 if (result == STATE_OK) { 313 if (result == STATE_OK) {
278 result = get_status(elapsed_time, time_thresholds); 314 result = get_status(elapsed_time, config.time_thresholds);
279 if (result == STATE_OK) { 315 if (result == STATE_OK) {
280 printf("DNS %s: ", _("OK")); 316 printf("DNS %s: ", _("OK"));
281 } else if (result == STATE_WARNING) { 317 } else if (result == STATE_WARNING) {
@@ -283,25 +319,39 @@ int main(int argc, char **argv) {
283 } else if (result == STATE_CRITICAL) { 319 } else if (result == STATE_CRITICAL) {
284 printf("DNS %s: ", _("CRITICAL")); 320 printf("DNS %s: ", _("CRITICAL"));
285 } 321 }
286 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time); 322 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time),
287 printf(_(". %s returns %s"), query_address, address); 323 elapsed_time);
288 if ((time_thresholds->warning != NULL) && (time_thresholds->critical != NULL)) { 324 printf(_(". %s returns %s"), config.query_address, address);
289 printf("|%s\n", fperfdata("time", elapsed_time, "s", true, time_thresholds->warning->end, true, time_thresholds->critical->end, 325 if ((config.time_thresholds->warning != NULL) &&
290 true, 0, false, 0)); 326 (config.time_thresholds->critical != NULL)) {
291 } else if ((time_thresholds->warning == NULL) && (time_thresholds->critical != NULL)) { 327 printf("|%s\n",
292 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, time_thresholds->critical->end, true, 0, false, 0)); 328 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
293 } else if ((time_thresholds->warning != NULL) && (time_thresholds->critical == NULL)) { 329 true, config.time_thresholds->critical->end, true, 0, false, 0));
294 printf("|%s\n", fperfdata("time", elapsed_time, "s", true, time_thresholds->warning->end, false, 0, true, 0, false, 0)); 330 } else if ((config.time_thresholds->warning == NULL) &&
295 } else 331 (config.time_thresholds->critical != NULL)) {
296 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); 332 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true,
297 } else if (result == STATE_WARNING) 333 config.time_thresholds->critical->end, true, 0, false, 0));
298 printf(_("DNS WARNING - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); 334 } else if ((config.time_thresholds->warning != NULL) &&
299 else if (result == STATE_CRITICAL) 335 (config.time_thresholds->critical == NULL)) {
300 printf(_("DNS CRITICAL - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); 336 printf("|%s\n",
301 else 337 fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end,
302 printf(_("DNS UNKNOWN - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); 338 false, 0, true, 0, false, 0));
303 339 } else {
304 return result; 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);
305} 355}
306 356
307bool ip_match_cidr(const char *addr, const char *cidr_ro) { 357bool ip_match_cidr(const char *addr, const char *cidr_ro) {
@@ -317,76 +367,87 @@ bool ip_match_cidr(const char *addr, const char *cidr_ro) {
317 mask = atoi(mask_c); 367 mask = atoi(mask_c);
318 368
319 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ 369 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */
320 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);
321} 372}
322 373
323unsigned long ip2long(const char *src) { 374unsigned long ip2long(const char *src) {
324 unsigned long ip[4]; 375 unsigned long ip[4];
325 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ 376 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */
326 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && 377 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 &&
327 ip[3] < 256) 378 ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && ip[3] < 256)
328 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3] 379 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
329 : 0; 380 : 0;
330} 381}
331 382
332int error_scan(char *input_buffer, bool *is_nxdomain) { 383mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain,
384 const char dns_server[ADDRESS_LENGTH]) {
333 385
334 const int nxdomain = strstr(input_buffer, "Non-existent") || strstr(input_buffer, "** server can't find") || 386 const int nxdomain = strstr(input_buffer, "Non-existent") ||
387 strstr(input_buffer, "** server can't find") ||
335 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN"); 388 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN");
336 if (nxdomain) 389 if (nxdomain) {
337 *is_nxdomain = true; 390 *is_nxdomain = true;
391 }
338 392
339 /* the DNS lookup timed out */ 393 /* the DNS lookup timed out */
340 if (strstr(input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) || 394 if (strstr(input_buffer,
341 strstr(input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || 395 _("Note: nslookup is deprecated and may be removed from future releases.")) ||
342 strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) 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."))) {
343 return STATE_OK; 399 return STATE_OK;
400 }
344 401
345 /* DNS server is not running... */ 402 /* DNS server is not running... */
346 else if (strstr(input_buffer, "No response from server")) 403 else if (strstr(input_buffer, "No response from server")) {
347 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); 404 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
348 else if (strstr(input_buffer, "no servers could be reached")) 405 } else if (strstr(input_buffer, "no servers could be reached")) {
349 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); 406 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
407 }
350 408
351 /* Host name is valid, but server doesn't have records... */ 409 /* Host name is valid, but server doesn't have records... */
352 else if (strstr(input_buffer, "No records")) 410 else if (strstr(input_buffer, "No records")) {
353 die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server); 411 die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
412 }
354 413
355 /* Connection was refused */ 414 /* Connection was refused */
356 else if (strstr(input_buffer, "Connection refused") || strstr(input_buffer, "Couldn't find server") || 415 else if (strstr(input_buffer, "Connection refused") ||
357 strstr(input_buffer, "Refused") || (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) 416 strstr(input_buffer, "Couldn't find server") || strstr(input_buffer, "Refused") ||
417 (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) {
358 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server); 418 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
419 }
359 420
360 /* Query refused (usually by an ACL in the namserver) */ 421 /* Query refused (usually by an ACL in the namserver) */
361 else if (strstr(input_buffer, "Query refused")) 422 else if (strstr(input_buffer, "Query refused")) {
362 die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server); 423 die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
424 }
363 425
364 /* No information (e.g. nameserver IP has two PTR records) */ 426 /* No information (e.g. nameserver IP has two PTR records) */
365 else if (strstr(input_buffer, "No information")) 427 else if (strstr(input_buffer, "No information")) {
366 die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server); 428 die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
429 }
367 430
368 /* Network is unreachable */ 431 /* Network is unreachable */
369 else if (strstr(input_buffer, "Network is unreachable")) 432 else if (strstr(input_buffer, "Network is unreachable")) {
370 die(STATE_CRITICAL, _("Network is unreachable\n")); 433 die(STATE_CRITICAL, _("Network is unreachable\n"));
434 }
371 435
372 /* Internal server failure */ 436 /* Internal server failure */
373 else if (strstr(input_buffer, "Server failure")) 437 else if (strstr(input_buffer, "Server failure")) {
374 die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server); 438 die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
439 }
375 440
376 /* Request error or the DNS lookup timed out */ 441 /* Request error or the DNS lookup timed out */
377 else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out")) 442 else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out")) {
378 return STATE_WARNING; 443 return STATE_WARNING;
444 }
379 445
380 return STATE_OK; 446 return STATE_OK;
381} 447}
382 448
383/* process command-line arguments */ 449/* process command-line arguments */
384int process_arguments(int argc, char **argv) { 450check_dns_config_wrapper process_arguments(int argc, char **argv) {
385 int c;
386 char *warning = NULL;
387 char *critical = NULL;
388
389 int opt_index = 0;
390 static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, 451 static struct option long_opts[] = {{"help", no_argument, 0, 'h'},
391 {"version", no_argument, 0, 'V'}, 452 {"version", no_argument, 0, 'V'},
392 {"verbose", no_argument, 0, 'v'}, 453 {"verbose", no_argument, 0, 'v'},
@@ -402,20 +463,34 @@ int process_arguments(int argc, char **argv) {
402 {"critical", required_argument, 0, 'c'}, 463 {"critical", required_argument, 0, 'c'},
403 {0, 0, 0, 0}}; 464 {0, 0, 0, 0}};
404 465
405 if (argc < 2) 466 check_dns_config_wrapper result = {
406 return ERROR; 467 .config = check_dns_config_init(),
468 .errorcode = OK,
469 };
407 470
408 for (c = 1; c < argc; c++) 471 if (argc < 2) {
409 if (strcmp("-to", argv[c]) == 0) 472 result.errorcode = ERROR;
410 strcpy(argv[c], "-t"); 473 return result;
474 }
411 475
412 while (1) { 476 for (int index = 1; index < argc; index++) {
413 c = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); 477 if (strcmp("-to", argv[index]) == 0) {
478 strcpy(argv[index], "-t");
479 }
480 }
481
482 char *warning = NULL;
483 char *critical = NULL;
484 int opt_index = 0;
485 int index = 0;
486 while (true) {
487 index = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index);
414 488
415 if (c == -1 || c == EOF) 489 if (index == -1 || index == EOF) {
416 break; 490 break;
491 }
417 492
418 switch (c) { 493 switch (index) {
419 case 'h': /* help */ 494 case 'h': /* help */
420 print_help(); 495 print_help();
421 exit(STATE_UNKNOWN); 496 exit(STATE_UNKNOWN);
@@ -429,54 +504,67 @@ int process_arguments(int argc, char **argv) {
429 timeout_interval = atoi(optarg); 504 timeout_interval = atoi(optarg);
430 break; 505 break;
431 case 'H': /* hostname */ 506 case 'H': /* hostname */
432 if (strlen(optarg) >= ADDRESS_LENGTH) 507 if (strlen(optarg) >= ADDRESS_LENGTH) {
433 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 508 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
434 strcpy(query_address, optarg); 509 }
510 strcpy(result.config.query_address, optarg);
435 break; 511 break;
436 case 's': /* server name */ 512 case 's': /* server name */
437 /* TODO: this host_or_die check is probably unnecessary. 513 /* TODO: this host_or_die check is probably unnecessary.
438 * Better to confirm nslookup response matches */ 514 * Better to confirm nslookup response matches */
439 host_or_die(optarg); 515 host_or_die(optarg);
440 if (strlen(optarg) >= ADDRESS_LENGTH) 516 if (strlen(optarg) >= ADDRESS_LENGTH) {
441 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 517 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
442 strcpy(dns_server, optarg); 518 }
519 strcpy(result.config.dns_server, optarg);
443 break; 520 break;
444 case 'r': /* reverse server name */ 521 case 'r': /* reverse server name */
445 /* TODO: Is this host_or_die necessary? */ 522 /* TODO: Is this host_or_die necessary? */
523 // TODO This does not do anything!!! 2025-03-08 rincewind
446 host_or_die(optarg); 524 host_or_die(optarg);
447 if (strlen(optarg) >= ADDRESS_LENGTH) 525 if (strlen(optarg) >= ADDRESS_LENGTH) {
448 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 526 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
527 }
528 static char ptr_server[ADDRESS_LENGTH] = "";
449 strcpy(ptr_server, optarg); 529 strcpy(ptr_server, optarg);
450 break; 530 break;
451 case 'a': /* expected address */ 531 case 'a': /* expected address */
452 if (strlen(optarg) >= ADDRESS_LENGTH) 532 if (strlen(optarg) >= ADDRESS_LENGTH) {
453 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 533 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
534 }
454 if (strchr(optarg, ',') != NULL) { 535 if (strchr(optarg, ',') != NULL) {
455 char *comma = strchr(optarg, ','); 536 char *comma = strchr(optarg, ',');
456 while (comma != NULL) { 537 while (comma != NULL) {
457 expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **)); 538 result.config.expected_address = (char **)realloc(
458 expected_address[expected_address_cnt] = strndup(optarg, comma - optarg); 539 result.config.expected_address,
459 expected_address_cnt++; 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++;
460 optarg = comma + 1; 544 optarg = comma + 1;
461 comma = strchr(optarg, ','); 545 comma = strchr(optarg, ',');
462 } 546 }
463 expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **)); 547 result.config.expected_address =
464 expected_address[expected_address_cnt] = strdup(optarg); 548 (char **)realloc(result.config.expected_address,
465 expected_address_cnt++; 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++;
466 } else { 552 } else {
467 expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **)); 553 result.config.expected_address =
468 expected_address[expected_address_cnt] = strdup(optarg); 554 (char **)realloc(result.config.expected_address,
469 expected_address_cnt++; 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++;
470 } 558 }
471 break; 559 break;
472 case 'n': /* expect NXDOMAIN */ 560 case 'n': /* expect NXDOMAIN */
473 expect_nxdomain = true; 561 result.config.expect_nxdomain = true;
474 break; 562 break;
475 case 'A': /* expect authority */ 563 case 'A': /* expect authority */
476 expect_authority = true; 564 result.config.expect_authority = true;
477 break; 565 break;
478 case 'L': /* all must match */ 566 case 'L': /* all must match */
479 all_match = true; 567 result.config.all_match = true;
480 break; 568 break;
481 case 'w': 569 case 'w':
482 warning = optarg; 570 warning = optarg;
@@ -489,38 +577,42 @@ int process_arguments(int argc, char **argv) {
489 } 577 }
490 } 578 }
491 579
492 c = optind; 580 index = optind;
493 if (strlen(query_address) == 0 && c < argc) { 581 if (strlen(result.config.query_address) == 0 && index < argc) {
494 if (strlen(argv[c]) >= ADDRESS_LENGTH) 582 if (strlen(argv[index]) >= ADDRESS_LENGTH) {
495 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 583 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
496 strcpy(query_address, argv[c++]); 584 }
585 strcpy(result.config.query_address, argv[index++]);
497 } 586 }
498 587
499 if (strlen(dns_server) == 0 && c < argc) { 588 if (strlen(result.config.dns_server) == 0 && index < argc) {
500 /* TODO: See -s option */ 589 /* TODO: See -s option */
501 host_or_die(argv[c]); 590 host_or_die(argv[index]);
502 if (strlen(argv[c]) >= ADDRESS_LENGTH) 591 if (strlen(argv[index]) >= ADDRESS_LENGTH) {
503 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 592 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
504 strcpy(dns_server, argv[c++]); 593 }
594 strcpy(result.config.dns_server, argv[index++]);
505 } 595 }
506 596
507 set_thresholds(&time_thresholds, warning, critical); 597 set_thresholds(&result.config.time_thresholds, warning, critical);
508 598
509 return validate_arguments(); 599 return validate_arguments(result);
510} 600}
511 601
512int validate_arguments(void) { 602check_dns_config_wrapper validate_arguments(check_dns_config_wrapper config_wrapper) {
513 if (query_address[0] == 0) { 603 if (config_wrapper.config.query_address[0] == 0) {
514 printf("missing --host argument\n"); 604 printf("missing --host argument\n");
515 return ERROR; 605 config_wrapper.errorcode = ERROR;
606 return config_wrapper;
516 } 607 }
517 608
518 if (expected_address_cnt > 0 && expect_nxdomain) { 609 if (config_wrapper.config.expected_address_cnt > 0 && config_wrapper.config.expect_nxdomain) {
519 printf("--expected-address and --expect-nxdomain cannot be combined\n"); 610 printf("--expected-address and --expect-nxdomain cannot be combined\n");
520 return ERROR; 611 config_wrapper.errorcode = ERROR;
612 return config_wrapper;
521 } 613 }
522 614
523 return OK; 615 return config_wrapper;
524} 616}
525 617
526void print_help(void) { 618void print_help(void) {
@@ -529,9 +621,11 @@ void print_help(void) {
529 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 621 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
530 printf(COPYRIGHT, copyright, email); 622 printf(COPYRIGHT, copyright, email);
531 623
532 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query.")); 624 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given "
625 "host/domain query."));
533 printf("%s\n", _("An optional DNS server to use may be specified.")); 626 printf("%s\n", _("An optional DNS server to use may be specified."));
534 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used.")); 627 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in "
628 "/etc/resolv.conf will be used."));
535 629
536 printf("\n\n"); 630 printf("\n\n");
537 631
@@ -545,11 +639,14 @@ void print_help(void) {
545 printf(" -s, --server=HOST\n"); 639 printf(" -s, --server=HOST\n");
546 printf(" %s\n", _("Optional DNS server you want to use for the lookup")); 640 printf(" %s\n", _("Optional DNS server you want to use for the lookup"));
547 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n"); 641 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
548 printf(" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); 642 printf(" %s\n",
549 printf(" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); 643 _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
644 printf(" %s\n",
645 _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
550 printf(" %s\n", _("value matches).")); 646 printf(" %s\n", _("value matches)."));
551 printf(" -n, --expect-nxdomain\n"); 647 printf(" -n, --expect-nxdomain\n");
552 printf(" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); 648 printf(" %s\n",
649 _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
553 printf(" %s\n", _("Cannot be used together with -a")); 650 printf(" %s\n", _("Cannot be used together with -a"));
554 printf(" -A, --expect-authority\n"); 651 printf(" -A, --expect-authority\n");
555 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); 652 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
@@ -558,7 +655,8 @@ void print_help(void) {
558 printf(" -c, --critical=seconds\n"); 655 printf(" -c, --critical=seconds\n");
559 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off")); 656 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
560 printf(" -L, --all\n"); 657 printf(" -L, --all\n");
561 printf(" %s\n", _("Return critical if the list of expected addresses does not match all addresses")); 658 printf(" %s\n",
659 _("Return critical if the list of expected addresses does not match all addresses"));
562 printf(" %s\n", _("returned. Default off")); 660 printf(" %s\n", _("returned. Default off"));
563 661
564 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 662 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
@@ -568,5 +666,7 @@ void print_help(void) {
568 666
569void print_usage(void) { 667void print_usage(void) {
570 printf("%s\n", _("Usage:")); 668 printf("%s\n", _("Usage:"));
571 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); 669 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c "
670 "crit] [-L]\n",
671 progname);
572} 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 19f6c046..602d5868 100644
--- a/plugins/check_dummy.c
+++ b/plugins/check_dummy.c
@@ -45,18 +45,19 @@ int main(int argc, char **argv) {
45 bindtextdomain(PACKAGE, LOCALEDIR); 45 bindtextdomain(PACKAGE, LOCALEDIR);
46 textdomain(PACKAGE); 46 textdomain(PACKAGE);
47 47
48 if (argc < 2) 48 if (argc < 2) {
49 usage4(_("Could not parse arguments")); 49 usage4(_("Could not parse arguments"));
50 else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) { 50 } else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) {
51 print_revision(progname, NP_VERSION); 51 print_revision(progname, NP_VERSION);
52 exit(STATE_UNKNOWN); 52 exit(STATE_UNKNOWN);
53 } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { 53 } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
54 print_help(); 54 print_help();
55 exit(STATE_UNKNOWN); 55 exit(STATE_UNKNOWN);
56 } else if (!is_integer(argv[1])) 56 } else if (!is_integer(argv[1])) {
57 usage4(_("Arguments to check_dummy must be an integer")); 57 usage4(_("Arguments to check_dummy must be an integer"));
58 else 58 } else {
59 result = atoi(argv[1]); 59 result = atoi(argv[1]);
60 }
60 61
61 switch (result) { 62 switch (result) {
62 case STATE_OK: 63 case STATE_OK:
@@ -78,8 +79,9 @@ int main(int argc, char **argv) {
78 return STATE_UNKNOWN; 79 return STATE_UNKNOWN;
79 } 80 }
80 81
81 if (argc >= 3) 82 if (argc >= 3) {
82 printf(": %s", argv[2]); 83 printf(": %s", argv[2]);
84 }
83 85
84 printf("\n"); 86 printf("\n");
85 87
@@ -92,7 +94,8 @@ void print_help(void) {
92 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 94 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
93 printf(COPYRIGHT, copyright, email); 95 printf(COPYRIGHT, copyright, email);
94 96
95 printf("%s\n", _("This plugin will simply return the state corresponding to the numeric value")); 97 printf("%s\n",
98 _("This plugin will simply return the state corresponding to the numeric value"));
96 99
97 printf("%s\n", _("of the <state> argument with optional text")); 100 printf("%s\n", _("of the <state> argument with optional text"));
98 101
diff --git a/plugins/check_fping.c b/plugins/check_fping.c
index c1d03ece..6160c2cb 100644
--- a/plugins/check_fping.c
+++ b/plugins/check_fping.c
@@ -38,52 +38,30 @@ const char *email = "devel@monitoring-plugins.org";
38#include "netutils.h" 38#include "netutils.h"
39#include "utils.h" 39#include "utils.h"
40#include <stdbool.h> 40#include <stdbool.h>
41#include "check_fping.d/config.h"
42#include "states.h"
41 43
42enum { 44enum {
43 PACKET_COUNT = 1,
44 PACKET_SIZE = 56,
45 PL = 0, 45 PL = 0,
46 RTA = 1 46 RTA = 1
47}; 47};
48 48
49static int textscan(char *buf); 49static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/,
50static int process_arguments(int /*argc*/, char ** /*argv*/); 50 double /*crta*/, bool /*wrta_p*/, double /*wrta*/, bool /*cpl_p*/,
51 int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/);
52
53typedef struct {
54 int errorcode;
55 check_fping_config config;
56} check_fping_config_wrapper;
57static check_fping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
51static int get_threshold(char *arg, char *rv[2]); 58static int get_threshold(char *arg, char *rv[2]);
52static void print_help(void); 59static void print_help(void);
53void print_usage(void); 60void print_usage(void);
54 61
55static char *server_name = NULL;
56static char *sourceip = NULL;
57static char *sourceif = NULL;
58static int packet_size = PACKET_SIZE;
59static int packet_count = PACKET_COUNT;
60static int target_timeout = 0;
61static int packet_interval = 0;
62static bool verbose = false; 62static bool verbose = false;
63static bool dontfrag = false;
64static bool randomize_packet_data = false;
65static int cpl;
66static int wpl;
67static double crta;
68static double wrta;
69static bool cpl_p = false;
70static bool wpl_p = false;
71static bool alive_p = false;
72static bool crta_p = false;
73static bool wrta_p = false;
74 63
75int main(int argc, char **argv) { 64int main(int argc, char **argv) {
76 /* normally should be int result = STATE_UNKNOWN; */
77
78 int status = STATE_UNKNOWN;
79 int result = 0;
80 char *fping_prog = NULL;
81 char *server = NULL;
82 char *command_line = NULL;
83 char *input_buffer = NULL;
84 char *option_string = "";
85 input_buffer = malloc(MAX_INPUT_BUFFER);
86
87 setlocale(LC_ALL, ""); 65 setlocale(LC_ALL, "");
88 bindtextdomain(PACKAGE, LOCALEDIR); 66 bindtextdomain(PACKAGE, LOCALEDIR);
89 textdomain(PACKAGE); 67 textdomain(PACKAGE);
@@ -91,39 +69,81 @@ int main(int argc, char **argv) {
91 /* Parse extra opts if any */ 69 /* Parse extra opts if any */
92 argv = np_extra_opts(&argc, argv, progname); 70 argv = np_extra_opts(&argc, argv, progname);
93 71
94 if (process_arguments(argc, argv) == ERROR) 72 check_fping_config_wrapper tmp_config = process_arguments(argc, argv);
73 if (tmp_config.errorcode == ERROR) {
95 usage4(_("Could not parse arguments")); 74 usage4(_("Could not parse arguments"));
75 }
76
77 const check_fping_config config = tmp_config.config;
78
79 char *server = NULL;
80 server = strscpy(server, config.server_name);
96 81
97 server = strscpy(server, server_name); 82 char *option_string = "";
83 char *fping_prog = NULL;
84
85 /* First determine if the target is dualstack or ipv6 only. */
86 bool server_is_inet6_addr = is_inet6_addr(server);
87
88 /*
89 * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack
90 * -> we use ipv6
91 * If the user requested -4 OR the user made no assertion and the address is v4 ONLY
92 * -> we use ipv4
93 */
94 if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) {
95 xasprintf(&option_string, "%s-6 ", option_string);
96 } else {
97 xasprintf(&option_string, "%s-4 ", option_string);
98 }
99 fping_prog = strdup(PATH_TO_FPING);
98 100
99 /* compose the command */ 101 /* compose the command */
100 if (target_timeout) 102 if (config.target_timeout) {
101 xasprintf(&option_string, "%s-t %d ", option_string, target_timeout); 103 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout);
102 if (packet_interval) 104 }
103 xasprintf(&option_string, "%s-p %d ", option_string, packet_interval); 105 if (config.packet_interval) {
104 if (sourceip) 106 xasprintf(&option_string, "%s-p %d ", option_string, config.packet_interval);
105 xasprintf(&option_string, "%s-S %s ", option_string, sourceip); 107 }
106 if (sourceif) 108 if (config.sourceip) {
107 xasprintf(&option_string, "%s-I %s ", option_string, sourceif); 109 xasprintf(&option_string, "%s-S %s ", option_string, config.sourceip);
108 if (dontfrag) 110 }
111 if (config.sourceif) {
112 xasprintf(&option_string, "%s-I %s ", option_string, config.sourceif);
113 }
114 if (config.dontfrag) {
109 xasprintf(&option_string, "%s-M ", option_string); 115 xasprintf(&option_string, "%s-M ", option_string);
110 if (randomize_packet_data) 116 }
117 if (config.randomize_packet_data) {
111 xasprintf(&option_string, "%s-R ", option_string); 118 xasprintf(&option_string, "%s-R ", option_string);
119 }
112 120
121 if (config.fwmark_set) {
122 xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark);
123 }
113 124
114#ifdef PATH_TO_FPING6 125 if (config.icmp_timestamp) {
115 if (address_family != AF_INET && is_inet6_addr(server)) 126 xasprintf(&option_string, "%s--icmp-timestamp ", option_string);
116 fping_prog = strdup(PATH_TO_FPING6); 127 }
117 else
118 fping_prog = strdup(PATH_TO_FPING);
119#else
120 fping_prog = strdup(PATH_TO_FPING);
121#endif
122 128
123 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, packet_size, packet_count, server); 129 if (config.check_source) {
130 xasprintf(&option_string, "%s--check-source ", option_string);
131 }
132
133 char *command_line = NULL;
124 134
125 if (verbose) 135 if (config.icmp_timestamp) {
136 // no packet size settable for ICMP timestamp
137 xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count,
138 server);
139 } else {
140 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string,
141 config.packet_size, config.packet_count, server);
142 }
143
144 if (verbose) {
126 printf("%s\n", command_line); 145 printf("%s\n", command_line);
146 }
127 147
128 /* run the command */ 148 /* run the command */
129 child_process = spopen(command_line); 149 child_process = spopen(command_line);
@@ -137,23 +157,31 @@ int main(int argc, char **argv) {
137 printf(_("Could not open stderr for %s\n"), command_line); 157 printf(_("Could not open stderr for %s\n"), command_line);
138 } 158 }
139 159
160 char *input_buffer = malloc(MAX_INPUT_BUFFER);
161 mp_state_enum status = STATE_UNKNOWN;
140 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 162 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
141 if (verbose) 163 if (verbose) {
142 printf("%s", input_buffer); 164 printf("%s", input_buffer);
143 status = max_state(status, textscan(input_buffer)); 165 }
166 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
167 config.crta, config.wrta_p, config.wrta, config.cpl_p,
168 config.cpl, config.wpl_p, config.wpl, config.alive_p));
144 } 169 }
145 170
146 /* If we get anything on STDERR, at least set warning */ 171 /* If we get anything on STDERR, at least set warning */
147 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 172 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
148 status = max_state(status, STATE_WARNING); 173 status = max_state(status, STATE_WARNING);
149 if (verbose) 174 if (verbose) {
150 printf("%s", input_buffer); 175 printf("%s", input_buffer);
151 status = max_state(status, textscan(input_buffer)); 176 }
177 status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p,
178 config.crta, config.wrta_p, config.wrta, config.cpl_p,
179 config.cpl, config.wpl_p, config.wpl, config.alive_p));
152 } 180 }
153 (void)fclose(child_stderr); 181 (void)fclose(child_stderr);
154 182
155 /* close the pipe */ 183 /* close the pipe */
156 result = spclose(child_process); 184 int result = spclose(child_process);
157 if (result) { 185 if (result) {
158 /* need to use max_state not max */ 186 /* need to use max_state not max */
159 status = max_state(status, STATE_WARNING); 187 status = max_state(status, STATE_WARNING);
@@ -172,21 +200,17 @@ int main(int argc, char **argv) {
172 } 200 }
173 } 201 }
174 202
175 printf("FPING %s - %s\n", state_text(status), server_name); 203 printf("FPING %s - %s\n", state_text(status), config.server_name);
176 204
177 return status; 205 return status;
178} 206}
179 207
180int textscan(char *buf) { 208mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p,
181 char *rtastr = NULL; 209 double wrta, bool cpl_p, int cpl, bool wpl_p, int wpl, bool alive_p) {
182 char *losstr = NULL;
183 char *xmtstr = NULL;
184 double loss;
185 double rta;
186 double xmt;
187 int status = STATE_UNKNOWN;
188
189 /* stops testing after the first successful reply. */ 210 /* stops testing after the first successful reply. */
211 double rta;
212 double loss;
213 char *rtastr = NULL;
190 if (alive_p && strstr(buf, "avg, 0% loss)")) { 214 if (alive_p && strstr(buf, "avg, 0% loss)")) {
191 rtastr = strstr(buf, "ms ("); 215 rtastr = strstr(buf, "ms (");
192 rtastr = 1 + index(rtastr, '('); 216 rtastr = 1 + index(rtastr, '(');
@@ -195,9 +219,14 @@ int textscan(char *buf) {
195 die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta, 219 die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta,
196 /* No loss since we only waited for the first reply 220 /* No loss since we only waited for the first reply
197 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ 221 perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */
198 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); 222 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
223 false, 0));
199 } 224 }
200 225
226 mp_state_enum status = STATE_UNKNOWN;
227 char *xmtstr = NULL;
228 double xmt;
229 char *losstr = NULL;
201 if (strstr(buf, "not found")) { 230 if (strstr(buf, "not found")) {
202 die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name); 231 die(STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name);
203 232
@@ -221,19 +250,22 @@ int textscan(char *buf) {
221 rtastr = 1 + index(rtastr, '/'); 250 rtastr = 1 + index(rtastr, '/');
222 loss = strtod(losstr, NULL); 251 loss = strtod(losstr, NULL);
223 rta = strtod(rtastr, NULL); 252 rta = strtod(rtastr, NULL);
224 if (cpl_p && loss > cpl) 253 if (cpl_p && loss > cpl) {
225 status = STATE_CRITICAL; 254 status = STATE_CRITICAL;
226 else if (crta_p && rta > crta) 255 } else if (crta_p && rta > crta) {
227 status = STATE_CRITICAL; 256 status = STATE_CRITICAL;
228 else if (wpl_p && loss > wpl) 257 } else if (wpl_p && loss > wpl) {
229 status = STATE_WARNING; 258 status = STATE_WARNING;
230 else if (wrta_p && rta > wrta) 259 } else if (wrta_p && rta > wrta) {
231 status = STATE_WARNING; 260 status = STATE_WARNING;
232 else 261 } else {
233 status = STATE_OK; 262 status = STATE_OK;
234 die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status), server_name, loss, rta, 263 }
235 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), 264 die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status),
236 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); 265 server_name, loss, rta,
266 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0),
267 fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0,
268 false, 0));
237 269
238 } else if (strstr(buf, "xmt/rcv/%loss")) { 270 } else if (strstr(buf, "xmt/rcv/%loss")) {
239 /* no min/max/avg if host was unreachable in fping v2.2.b1 */ 271 /* no min/max/avg if host was unreachable in fping v2.2.b1 */
@@ -241,22 +273,24 @@ int textscan(char *buf) {
241 losstr = strstr(buf, "="); 273 losstr = strstr(buf, "=");
242 xmtstr = 1 + losstr; 274 xmtstr = 1 + losstr;
243 xmt = strtod(xmtstr, NULL); 275 xmt = strtod(xmtstr, NULL);
244 if (xmt == 0) 276 if (xmt == 0) {
245 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name); 277 die(STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
278 }
246 losstr = 1 + strstr(losstr, "/"); 279 losstr = 1 + strstr(losstr, "/");
247 losstr = 1 + strstr(losstr, "/"); 280 losstr = 1 + strstr(losstr, "/");
248 loss = strtod(losstr, NULL); 281 loss = strtod(losstr, NULL);
249 if (atoi(losstr) == 100) 282 if (atoi(losstr) == 100) {
250 status = STATE_CRITICAL; 283 status = STATE_CRITICAL;
251 else if (cpl_p && loss > cpl) 284 } else if (cpl_p && loss > cpl) {
252 status = STATE_CRITICAL; 285 status = STATE_CRITICAL;
253 else if (wpl_p && loss > wpl) 286 } else if (wpl_p && loss > wpl) {
254 status = STATE_WARNING; 287 status = STATE_WARNING;
255 else 288 } else {
256 status = STATE_OK; 289 status = STATE_OK;
290 }
257 /* loss=%.0f%%;%d;%d;0;100 */ 291 /* loss=%.0f%%;%d;%d;0;100 */
258 die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss, 292 die(status, _("FPING %s - %s (loss=%.0f%% )|%s\n"), state_text(status), server_name, loss,
259 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100)); 293 perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0));
260 294
261 } else { 295 } else {
262 status = max_state(status, STATE_WARNING); 296 status = max_state(status, STATE_WARNING);
@@ -266,11 +300,12 @@ int textscan(char *buf) {
266} 300}
267 301
268/* process command-line arguments */ 302/* process command-line arguments */
269int process_arguments(int argc, char **argv) { 303check_fping_config_wrapper process_arguments(int argc, char **argv) {
270 int c; 304 enum {
271 char *rv[2]; 305 FWMARK_OPT = CHAR_MAX + 1,
272 306 ICMP_TIMESTAMP_OPT,
273 int option = 0; 307 CHECK_SOURCE_OPT,
308 };
274 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 309 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
275 {"sourceip", required_argument, 0, 'S'}, 310 {"sourceip", required_argument, 0, 'S'},
276 {"sourceif", required_argument, 0, 'I'}, 311 {"sourceif", required_argument, 0, 'I'},
@@ -288,32 +323,53 @@ int process_arguments(int argc, char **argv) {
288 {"use-ipv6", no_argument, 0, '6'}, 323 {"use-ipv6", no_argument, 0, '6'},
289 {"dontfrag", no_argument, 0, 'M'}, 324 {"dontfrag", no_argument, 0, 'M'},
290 {"random", no_argument, 0, 'R'}, 325 {"random", no_argument, 0, 'R'},
326#ifdef FPING_VERSION_5_2_OR_HIGHER
327 // only available with fping version >= 5.2
328 {"fwmark", required_argument, NULL, FWMARK_OPT},
329# ifdef FPING_VERSION_5_3_OR_HIGHER
330 // only available with fping version >= 5.3
331 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT},
332 {"check-source", no_argument, NULL, CHECK_SOURCE_OPT},
333# endif
334#endif
291 {0, 0, 0, 0}}; 335 {0, 0, 0, 0}};
292 336
337 char *rv[2];
293 rv[PL] = NULL; 338 rv[PL] = NULL;
294 rv[RTA] = NULL; 339 rv[RTA] = NULL;
295 340
296 if (argc < 2) 341 int option = 0;
297 return ERROR; 342
343 check_fping_config_wrapper result = {
344 .errorcode = OK,
345 .config = check_fping_config_init(),
346 };
347
348 if (argc < 2) {
349 result.errorcode = ERROR;
350 return result;
351 }
298 352
299 if (!is_option(argv[1])) { 353 if (!is_option(argv[1])) {
300 server_name = argv[1]; 354 result.config.server_name = argv[1];
301 argv[1] = argv[0]; 355 argv[1] = argv[0];
302 argv = &argv[1]; 356 argv = &argv[1];
303 argc--; 357 argc--;
304 } 358 }
305 359
306 while (1) { 360 while (true) {
307 c = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); 361 int option_index =
362 getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option);
308 363
309 if (c == -1 || c == EOF || c == 1) 364 if (option_index == -1 || option_index == EOF || option_index == 1) {
310 break; 365 break;
366 }
311 367
312 switch (c) { 368 switch (option_index) {
313 case '?': /* print short usage statement if args not parsable */ 369 case '?': /* print short usage statement if args not parsable */
314 usage5(); 370 usage5();
315 case 'a': /* host alive mode */ 371 case 'a': /* host alive mode */
316 alive_p = true; 372 result.config.alive_p = true;
317 break; 373 break;
318 case 'h': /* help */ 374 case 'h': /* help */
319 print_help(); 375 print_help();
@@ -325,109 +381,128 @@ int process_arguments(int argc, char **argv) {
325 verbose = true; 381 verbose = true;
326 break; 382 break;
327 case 'H': /* hostname */ 383 case 'H': /* hostname */
328 if (is_host(optarg) == false) { 384 if (!is_host(optarg)) {
329 usage2(_("Invalid hostname/address"), optarg); 385 usage2(_("Invalid hostname/address"), optarg);
330 } 386 }
331 server_name = strscpy(server_name, optarg); 387 result.config.server_name = optarg;
332 break; 388 break;
333 case 'S': /* sourceip */ 389 case 'S': /* sourceip */
334 if (is_host(optarg) == false) { 390 if (!is_host(optarg)) {
335 usage2(_("Invalid hostname/address"), optarg); 391 usage2(_("Invalid hostname/address"), optarg);
336 } 392 }
337 sourceip = strscpy(sourceip, optarg); 393 result.config.sourceip = optarg;
338 break; 394 break;
339 case 'I': /* sourceip */ 395 case 'I': /* sourceip */
340 sourceif = strscpy(sourceif, optarg); 396 result.config.sourceif = optarg;
341 break; 397 break;
342 case '4': /* IPv4 only */ 398 case '4': /* IPv4 only */
343 address_family = AF_INET; 399 address_family = AF_INET;
344 break; 400 break;
345 case '6': /* IPv6 only */ 401 case '6': /* IPv6 only */
346#ifdef USE_IPV6
347 address_family = AF_INET6; 402 address_family = AF_INET6;
348#else
349 usage(_("IPv6 support not available\n"));
350#endif
351 break; 403 break;
352 case 'c': 404 case 'c':
353 get_threshold(optarg, rv); 405 get_threshold(optarg, rv);
354 if (rv[RTA]) { 406 if (rv[RTA]) {
355 crta = strtod(rv[RTA], NULL); 407 result.config.crta = strtod(rv[RTA], NULL);
356 crta_p = true; 408 result.config.crta_p = true;
357 rv[RTA] = NULL; 409 rv[RTA] = NULL;
358 } 410 }
359 if (rv[PL]) { 411 if (rv[PL]) {
360 cpl = atoi(rv[PL]); 412 result.config.cpl = atoi(rv[PL]);
361 cpl_p = true; 413 result.config.cpl_p = true;
362 rv[PL] = NULL; 414 rv[PL] = NULL;
363 } 415 }
364 break; 416 break;
365 case 'w': 417 case 'w':
366 get_threshold(optarg, rv); 418 get_threshold(optarg, rv);
367 if (rv[RTA]) { 419 if (rv[RTA]) {
368 wrta = strtod(rv[RTA], NULL); 420 result.config.wrta = strtod(rv[RTA], NULL);
369 wrta_p = true; 421 result.config.wrta_p = true;
370 rv[RTA] = NULL; 422 rv[RTA] = NULL;
371 } 423 }
372 if (rv[PL]) { 424 if (rv[PL]) {
373 wpl = atoi(rv[PL]); 425 result.config.wpl = atoi(rv[PL]);
374 wpl_p = true; 426 result.config.wpl_p = true;
375 rv[PL] = NULL; 427 rv[PL] = NULL;
376 } 428 }
377 break; 429 break;
378 case 'b': /* bytes per packet */ 430 case 'b': /* bytes per packet */
379 if (is_intpos(optarg)) 431 if (is_intpos(optarg)) {
380 packet_size = atoi(optarg); 432 result.config.packet_size = atoi(optarg);
381 else 433 } else {
382 usage(_("Packet size must be a positive integer")); 434 usage(_("Packet size must be a positive integer"));
435 }
383 break; 436 break;
384 case 'n': /* number of packets */ 437 case 'n': /* number of packets */
385 if (is_intpos(optarg)) 438 if (is_intpos(optarg)) {
386 packet_count = atoi(optarg); 439 result.config.packet_count = atoi(optarg);
387 else 440 } else {
388 usage(_("Packet count must be a positive integer")); 441 usage(_("Packet count must be a positive integer"));
442 }
389 break; 443 break;
390 case 'T': /* timeout in msec */ 444 case 'T': /* timeout in msec */
391 if (is_intpos(optarg)) 445 if (is_intpos(optarg)) {
392 target_timeout = atoi(optarg); 446 result.config.target_timeout = atoi(optarg);
393 else 447 } else {
394 usage(_("Target timeout must be a positive integer")); 448 usage(_("Target timeout must be a positive integer"));
449 }
395 break; 450 break;
396 case 'i': /* interval in msec */ 451 case 'i': /* interval in msec */
397 if (is_intpos(optarg)) 452 if (is_intpos(optarg)) {
398 packet_interval = atoi(optarg); 453 result.config.packet_interval = atoi(optarg);
399 else 454 } else {
400 usage(_("Interval must be a positive integer")); 455 usage(_("Interval must be a positive integer"));
456 }
401 break; 457 break;
402 case 'R': 458 case 'R':
403 randomize_packet_data = true; 459 result.config.randomize_packet_data = true;
404 break; 460 break;
405 case 'M': 461 case 'M':
406 dontfrag = true; 462 result.config.dontfrag = true;
463 break;
464 case FWMARK_OPT:
465 if (is_intpos(optarg)) {
466 result.config.fwmark = (unsigned int)atol(optarg);
467 result.config.fwmark_set = true;
468 } else {
469 usage(_("fwmark must be a positive integer"));
470 }
471 break;
472 case ICMP_TIMESTAMP_OPT:
473 result.config.icmp_timestamp = true;
474 break;
475 case CHECK_SOURCE_OPT:
476 result.config.check_source = true;
407 break; 477 break;
408 } 478 }
409 } 479 }
410 480
411 if (server_name == NULL) 481 if (result.config.server_name == NULL) {
412 usage4(_("Hostname was not supplied")); 482 usage4(_("Hostname was not supplied"));
483 }
413 484
414 return OK; 485 return result;
415} 486}
416 487
417int get_threshold(char *arg, char *rv[2]) { 488int get_threshold(char *arg, char *rv[2]) {
418 char *arg1 = NULL;
419 char *arg2 = NULL; 489 char *arg2 = NULL;
420 490
421 arg1 = strscpy(arg1, arg); 491 char *arg1 = strdup(arg);
422 if (strpbrk(arg1, ",:")) 492 if (strpbrk(arg1, ",:")) {
423 arg2 = 1 + strpbrk(arg1, ",:"); 493 arg2 = 1 + strpbrk(arg1, ",:");
494 }
424 495
425 if (arg2) { 496 if (arg2) {
426 arg1[strcspn(arg1, ",:")] = 0; 497 arg1[strcspn(arg1, ",:")] = 0;
427 if (strstr(arg1, "%") && strstr(arg2, "%")) 498 if (strstr(arg1, "%") && strstr(arg2, "%")) {
428 die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname, arg); 499 die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname,
429 if (!strstr(arg1, "%") && !strstr(arg2, "%")) 500 arg);
430 die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname, arg); 501 }
502 if (!strstr(arg1, "%") && !strstr(arg2, "%")) {
503 die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname,
504 arg);
505 }
431 } 506 }
432 507
433 if (arg2 && strstr(arg2, "%")) { 508 if (arg2 && strstr(arg2, "%")) {
@@ -452,7 +527,8 @@ void print_help(void) {
452 printf("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n"); 527 printf("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n");
453 printf(COPYRIGHT, copyright, email); 528 printf(COPYRIGHT, copyright, email);
454 529
455 printf("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check")); 530 printf("%s\n",
531 _("This plugin will use the fping command to ping the specified host for a fast check"));
456 532
457 printf("%s\n", _("Note that it is necessary to set the suid flag on fping.")); 533 printf("%s\n", _("Note that it is necessary to set the suid flag on fping."));
458 534
@@ -466,7 +542,8 @@ void print_help(void) {
466 printf(UT_IPv46); 542 printf(UT_IPv46);
467 543
468 printf(" %s\n", "-H, --hostname=HOST"); 544 printf(" %s\n", "-H, --hostname=HOST");
469 printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)")); 545 printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, "
546 "reducing system load)"));
470 printf(" %s\n", "-w, --warning=THRESHOLD"); 547 printf(" %s\n", "-w, --warning=THRESHOLD");
471 printf(" %s\n", _("warning threshold pair")); 548 printf(" %s\n", _("warning threshold pair"));
472 printf(" %s\n", "-c, --critical=THRESHOLD"); 549 printf(" %s\n", "-c, --critical=THRESHOLD");
@@ -480,7 +557,8 @@ void print_help(void) {
480 printf(" %s\n", "-T, --target-timeout=INTEGER"); 557 printf(" %s\n", "-T, --target-timeout=INTEGER");
481 printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); 558 printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)"));
482 printf(" %s\n", "-i, --interval=INTEGER"); 559 printf(" %s\n", "-i, --interval=INTEGER");
483 printf(" %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets")); 560 printf(" %s (default: fping's default for -p)\n",
561 _("Interval (ms) between sending packets"));
484 printf(" %s\n", "-S, --sourceip=HOST"); 562 printf(" %s\n", "-S, --sourceip=HOST");
485 printf(" %s\n", _("name or IP Address of sourceip")); 563 printf(" %s\n", _("name or IP Address of sourceip"));
486 printf(" %s\n", "-I, --sourceif=IF"); 564 printf(" %s\n", "-I, --sourceif=IF");
@@ -489,9 +567,20 @@ void print_help(void) {
489 printf(" %s\n", _("set the Don't Fragment flag")); 567 printf(" %s\n", _("set the Don't Fragment flag"));
490 printf(" %s\n", "-R, --random"); 568 printf(" %s\n", "-R, --random");
491 printf(" %s\n", _("random packet data (to foil link data compression)")); 569 printf(" %s\n", _("random packet data (to foil link data compression)"));
570#ifdef FPING_VERSION_5_2_OR_HIGHER
571 printf(" %s\n", "--fwmark=INTEGER");
572 printf(" %s\n", _("set the routing mark to INTEGER (fping option)"));
573# ifdef FPING_VERSION_5_3_OR_HIGHER
574 printf(" %s\n", "--icmp-timestamp");
575 printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)"));
576 printf(" %s\n", "--check-source");
577 printf(" %s\n", _("discard replies not from target address (fping option)"));
578# endif
579#endif
492 printf(UT_VERBOSE); 580 printf(UT_VERBOSE);
493 printf("\n"); 581 printf("\n");
494 printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); 582 printf(" %s\n",
583 _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
495 printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of")); 584 printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of"));
496 printf(" %s\n", _("packet loss to trigger an alarm state.")); 585 printf(" %s\n", _("packet loss to trigger an alarm state."));
497 586
@@ -503,5 +592,6 @@ void print_help(void) {
503 592
504void print_usage(void) { 593void print_usage(void) {
505 printf("%s\n", _("Usage:")); 594 printf("%s\n", _("Usage:"));
506 printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname); 595 printf(" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n",
596 progname);
507} 597}
diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h
new file mode 100644
index 00000000..d3e50565
--- /dev/null
+++ b/plugins/check_fping.d/config.h
@@ -0,0 +1,81 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PACKET_SIZE = 56,
8 PACKET_COUNT = 1,
9};
10
11typedef struct {
12 char *server_name;
13 char *sourceip;
14 char *sourceif;
15 int packet_size;
16 int packet_count;
17 int target_timeout;
18 int packet_interval;
19 bool randomize_packet_data;
20 bool dontfrag;
21 bool alive_p;
22
23 double crta;
24 bool crta_p;
25 double wrta;
26 bool wrta_p;
27
28 int cpl;
29 bool cpl_p;
30 int wpl;
31 bool wpl_p;
32
33 // only available with fping version >= 5.2
34 // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark
35 // in the packets
36 unsigned int fwmark;
37 bool fwmark_set;
38
39 // only available with fping version >= 5.3
40 // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead
41 // of ICMP Echo
42 bool icmp_timestamp;
43
44 // Setting check_source lets fping discard replies which are not from the target address
45 bool check_source;
46} check_fping_config;
47
48check_fping_config check_fping_config_init() {
49 check_fping_config tmp = {
50 .server_name = NULL,
51 .sourceip = NULL,
52 .sourceif = NULL,
53 .packet_size = PACKET_SIZE,
54 .packet_count = PACKET_COUNT,
55 .target_timeout = 0,
56 .packet_interval = 0,
57 .randomize_packet_data = false,
58 .dontfrag = false,
59 .alive_p = false,
60
61 .crta = 0,
62 .crta_p = false,
63 .wrta = 0,
64 .wrta_p = false,
65
66 .cpl = 0,
67 .cpl_p = false,
68 .wpl = 0,
69 .wpl_p = false,
70
71 // only available with fping version >= 5.2
72 .fwmark = 0,
73 .fwmark_set = false, // just to be deterministic
74
75 // only available with fping version >= 5.3
76 .icmp_timestamp = false,
77 .check_source = false,
78
79 };
80 return tmp;
81}
diff --git a/plugins/check_game.c b/plugins/check_game.c
index 619277e7..974a7253 100644
--- a/plugins/check_game.c
+++ b/plugins/check_game.c
@@ -36,9 +36,15 @@ const char *email = "devel@monitoring-plugins.org";
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
40static int process_arguments(int /*argc*/, char ** /*argv*/); 42typedef struct {
41static int validate_arguments(void); 43 int errorcode;
44 check_game_config config;
45} check_game_config_wrapper;
46
47static check_game_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
42static void print_help(void); 48static void print_help(void);
43void print_usage(void); 49void print_usage(void);
44 50
@@ -49,26 +55,9 @@ void print_usage(void);
49#define QSTAT_HOST_TIMEOUT "TIMEOUT" 55#define QSTAT_HOST_TIMEOUT "TIMEOUT"
50#define QSTAT_MAX_RETURN_ARGS 12 56#define QSTAT_MAX_RETURN_ARGS 12
51 57
52static char *server_ip;
53static char *game_type;
54static int port = 0;
55
56static bool verbose = false; 58static bool verbose = false;
57 59
58static int qstat_game_players_max = -1;
59static int qstat_game_players = -1;
60static int qstat_game_field = -1;
61static int qstat_map_field = -1;
62static int qstat_ping_field = -1;
63
64int main(int argc, char **argv) { 60int main(int argc, char **argv) {
65 char *command_line;
66 int result = STATE_UNKNOWN;
67 char *p;
68 char *ret[QSTAT_MAX_RETURN_ARGS];
69 size_t i = 0;
70 output chld_out;
71
72 setlocale(LC_ALL, ""); 61 setlocale(LC_ALL, "");
73 bindtextdomain(PACKAGE, LOCALEDIR); 62 bindtextdomain(PACKAGE, LOCALEDIR);
74 textdomain(PACKAGE); 63 textdomain(PACKAGE);
@@ -76,22 +65,32 @@ int main(int argc, char **argv) {
76 /* Parse extra opts if any */ 65 /* Parse extra opts if any */
77 argv = np_extra_opts(&argc, argv, progname); 66 argv = np_extra_opts(&argc, argv, progname);
78 67
79 if (process_arguments(argc, argv) == ERROR) 68 check_game_config_wrapper tmp = process_arguments(argc, argv);
69
70 if (tmp.errorcode == ERROR) {
80 usage_va(_("Could not parse arguments")); 71 usage_va(_("Could not parse arguments"));
72 }
73
74 check_game_config config = tmp.config;
81 75
82 result = STATE_OK; 76 mp_state_enum result = STATE_OK;
83 77
84 /* create the command line to execute */ 78 /* create the command line to execute */
85 xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip); 79 char *command_line = NULL;
80 xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER,
81 config.game_type, config.server_ip);
86 82
87 if (port) 83 if (config.port) {
88 xasprintf(&command_line, "%s:%-d", command_line, port); 84 xasprintf(&command_line, "%s:%-d", command_line, config.port);
85 }
89 86
90 if (verbose) 87 if (verbose) {
91 printf("%s\n", command_line); 88 printf("%s\n", command_line);
89 }
92 90
93 /* run the command. historically, this plugin ignores output on stderr, 91 /* run the command. historically, this plugin ignores output on stderr,
94 * as well as return status of the qstat program */ 92 * as well as return status of the qstat program */
93 output chld_out = {};
95 (void)np_runcmd(command_line, &chld_out, NULL, 0); 94 (void)np_runcmd(command_line, &chld_out, NULL, 0);
96 95
97 /* sanity check */ 96 /* sanity check */
@@ -104,19 +103,22 @@ int main(int argc, char **argv) {
104 In the end, I figured I'd simply let an error occur & then trap it 103 In the end, I figured I'd simply let an error occur & then trap it
105 */ 104 */
106 105
107 if (!strncmp(chld_out.line[0], "unknown option", 14)) { 106 if (!strncmp(chld_out.line[0], "unknown option", strlen("unknown option"))) {
108 printf(_("CRITICAL - Host type parameter incorrect!\n")); 107 printf(_("CRITICAL - Host type parameter incorrect!\n"));
109 result = STATE_CRITICAL; 108 result = STATE_CRITICAL;
110 return result; 109 exit(result);
111 } 110 }
112 111
113 p = (char *)strtok(chld_out.line[0], QSTAT_DATA_DELIMITER); 112 char *ret[QSTAT_MAX_RETURN_ARGS];
114 while (p != NULL) { 113 size_t i = 0;
115 ret[i] = p; 114 char *sequence = strtok(chld_out.line[0], QSTAT_DATA_DELIMITER);
116 p = (char *)strtok(NULL, QSTAT_DATA_DELIMITER); 115 while (sequence != NULL) {
116 ret[i] = sequence;
117 sequence = strtok(NULL, QSTAT_DATA_DELIMITER);
117 i++; 118 i++;
118 if (i >= QSTAT_MAX_RETURN_ARGS) 119 if (i >= QSTAT_MAX_RETURN_ARGS) {
119 break; 120 break;
121 }
120 } 122 }
121 123
122 if (strstr(ret[2], QSTAT_HOST_ERROR)) { 124 if (strstr(ret[2], QSTAT_HOST_ERROR)) {
@@ -129,52 +131,66 @@ int main(int argc, char **argv) {
129 printf(_("CRITICAL - Game server timeout\n")); 131 printf(_("CRITICAL - Game server timeout\n"));
130 result = STATE_CRITICAL; 132 result = STATE_CRITICAL;
131 } else { 133 } else {
132 printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[qstat_game_players], ret[qstat_game_players_max], ret[qstat_game_field], 134 printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players],
133 ret[qstat_map_field], ret[qstat_ping_field], 135 ret[config.qstat_game_players_max], ret[config.qstat_game_field],
134 perfdata("players", atol(ret[qstat_game_players]), "", false, 0, false, 0, true, 0, true, atol(ret[qstat_game_players_max])), 136 ret[config.qstat_map_field], ret[config.qstat_ping_field],
135 fperfdata("ping", strtod(ret[qstat_ping_field], NULL), "", false, 0, false, 0, true, 0, false, 0)); 137 perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0,
138 true, 0, true, atol(ret[config.qstat_game_players_max])),
139 fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0,
140 true, 0, false, 0));
136 } 141 }
137 142
138 return result; 143 exit(result);
139} 144}
140 145
141int process_arguments(int argc, char **argv) { 146#define players_field_index 129
142 int c; 147#define max_players_field_index 130
148
149check_game_config_wrapper process_arguments(int argc, char **argv) {
150 static struct option long_opts[] = {
151 {"help", no_argument, 0, 'h'},
152 {"version", no_argument, 0, 'V'},
153 {"verbose", no_argument, 0, 'v'},
154 {"timeout", required_argument, 0, 't'},
155 {"hostname", required_argument, 0, 'H'},
156 {"port", required_argument, 0, 'P'},
157 {"game-type", required_argument, 0, 'G'},
158 {"map-field", required_argument, 0, 'm'},
159 {"ping-field", required_argument, 0, 'p'},
160 {"game-field", required_argument, 0, 'g'},
161 {"players-field", required_argument, 0, players_field_index},
162 {"max-players-field", required_argument, 0, max_players_field_index},
163 {0, 0, 0, 0}};
164
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 }
143 174
144 int opt_index = 0; 175 for (int option_counter = 1; option_counter < argc; option_counter++) {
145 static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, 176 if (strcmp("-mf", argv[option_counter]) == 0) {
146 {"version", no_argument, 0, 'V'}, 177 strcpy(argv[option_counter], "-m");
147 {"verbose", no_argument, 0, 'v'}, 178 } else if (strcmp("-pf", argv[option_counter]) == 0) {
148 {"timeout", required_argument, 0, 't'}, 179 strcpy(argv[option_counter], "-p");
149 {"hostname", required_argument, 0, 'H'}, 180 } else if (strcmp("-gf", argv[option_counter]) == 0) {
150 {"port", required_argument, 0, 'P'}, 181 strcpy(argv[option_counter], "-g");
151 {"game-type", required_argument, 0, 'G'}, 182 }
152 {"map-field", required_argument, 0, 'm'},
153 {"ping-field", required_argument, 0, 'p'},
154 {"game-field", required_argument, 0, 'g'},
155 {"players-field", required_argument, 0, 129},
156 {"max-players-field", required_argument, 0, 130},
157 {0, 0, 0, 0}};
158
159 if (argc < 2)
160 return ERROR;
161
162 for (c = 1; c < argc; c++) {
163 if (strcmp("-mf", argv[c]) == 0)
164 strcpy(argv[c], "-m");
165 else if (strcmp("-pf", argv[c]) == 0)
166 strcpy(argv[c], "-p");
167 else if (strcmp("-gf", argv[c]) == 0)
168 strcpy(argv[c], "-g");
169 } 183 }
170 184
171 while (1) { 185 int opt_index = 0;
172 c = getopt_long(argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index); 186 while (true) {
187 int option_index = getopt_long(argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index);
173 188
174 if (c == -1 || c == EOF) 189 if (option_index == -1 || option_index == EOF) {
175 break; 190 break;
191 }
176 192
177 switch (c) { 193 switch (option_index) {
178 case 'h': /* help */ 194 case 'h': /* help */
179 print_help(); 195 print_help();
180 exit(STATE_UNKNOWN); 196 exit(STATE_UNKNOWN);
@@ -188,79 +204,80 @@ int process_arguments(int argc, char **argv) {
188 timeout_interval = atoi(optarg); 204 timeout_interval = atoi(optarg);
189 break; 205 break;
190 case 'H': /* hostname */ 206 case 'H': /* hostname */
191 if (strlen(optarg) >= MAX_HOST_ADDRESS_LENGTH) 207 if (strlen(optarg) >= MAX_HOST_ADDRESS_LENGTH) {
192 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 208 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
193 server_ip = optarg; 209 }
210 result.config.server_ip = optarg;
194 break; 211 break;
195 case 'P': /* port */ 212 case 'P': /* port */
196 port = atoi(optarg); 213 result.config.port = atoi(optarg);
197 break; 214 break;
198 case 'G': /* hostname */ 215 case 'G': /* hostname */
199 if (strlen(optarg) >= MAX_INPUT_BUFFER) 216 if (strlen(optarg) >= MAX_INPUT_BUFFER) {
200 die(STATE_UNKNOWN, _("Input buffer overflow\n")); 217 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
201 game_type = optarg; 218 }
219 result.config.game_type = optarg;
202 break; 220 break;
203 case 'p': /* index of ping field */ 221 case 'p': /* index of ping field */
204 qstat_ping_field = atoi(optarg); 222 result.config.qstat_ping_field = atoi(optarg);
205 if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS) 223 if (result.config.qstat_ping_field < 0 ||
206 return ERROR; 224 result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) {
225 result.errorcode = ERROR;
226 return result;
227 }
207 break; 228 break;
208 case 'm': /* index on map field */ 229 case 'm': /* index on map field */
209 qstat_map_field = atoi(optarg); 230 result.config.qstat_map_field = atoi(optarg);
210 if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS) 231 if (result.config.qstat_map_field < 0 ||
211 return ERROR; 232 result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) {
233 result.errorcode = ERROR;
234 return result;
235 }
212 break; 236 break;
213 case 'g': /* index of game field */ 237 case 'g': /* index of game field */
214 qstat_game_field = atoi(optarg); 238 result.config.qstat_game_field = atoi(optarg);
215 if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS) 239 if (result.config.qstat_game_field < 0 ||
216 return ERROR; 240 result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) {
241 result.errorcode = ERROR;
242 return result;
243 }
217 break; 244 break;
218 case 129: /* index of player count field */ 245 case players_field_index: /* index of player count field */
219 qstat_game_players = atoi(optarg); 246 result.config.qstat_game_players = atoi(optarg);
220 if (qstat_game_players_max == 0) 247 if (result.config.qstat_game_players_max == 0) {
221 qstat_game_players_max = qstat_game_players - 1; 248 result.config.qstat_game_players_max = result.config.qstat_game_players - 1;
222 if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS) 249 }
223 return ERROR; 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 }
224 break; 255 break;
225 case 130: /* index of max players field */ 256 case max_players_field_index: /* index of max players field */
226 qstat_game_players_max = atoi(optarg); 257 result.config.qstat_game_players_max = atoi(optarg);
227 if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) 258 if (result.config.qstat_game_players_max < 0 ||
228 return ERROR; 259 result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) {
260 result.errorcode = ERROR;
261 return result;
262 }
229 break; 263 break;
230 default: /* args not parsable */ 264 default: /* args not parsable */
231 usage5(); 265 usage5();
232 } 266 }
233 } 267 }
234 268
235 c = optind; 269 int option_counter = optind;
236 /* first option is the game type */ 270 /* first option is the game type */
237 if (!game_type && c < argc) 271 if (!result.config.game_type && option_counter < argc) {
238 game_type = strdup(argv[c++]); 272 result.config.game_type = strdup(argv[option_counter++]);
273 }
239 274
240 /* Second option is the server name */ 275 /* Second option is the server name */
241 if (!server_ip && c < argc) 276 if (!result.config.server_ip && option_counter < argc) {
242 server_ip = strdup(argv[c++]); 277 result.config.server_ip = strdup(argv[option_counter++]);
243 278 }
244 return validate_arguments();
245}
246
247int validate_arguments(void) {
248 if (qstat_game_players_max < 0)
249 qstat_game_players_max = 4;
250
251 if (qstat_game_players < 0)
252 qstat_game_players = 5;
253
254 if (qstat_game_field < 0)
255 qstat_game_field = 2;
256
257 if (qstat_map_field < 0)
258 qstat_map_field = 3;
259
260 if (qstat_ping_field < 0)
261 qstat_ping_field = 5;
262 279
263 return OK; 280 return result;
264} 281}
265 282
266void print_help(void) { 283void print_help(void) {
@@ -277,22 +294,25 @@ void print_help(void) {
277 294
278 printf(UT_HELP_VRSN); 295 printf(UT_HELP_VRSN);
279 printf(UT_EXTRA_OPTS); 296 printf(UT_EXTRA_OPTS);
280 297 printf(" -H, --hostname=ADDRESS\n"
281 printf(" %s\n", "-p"); 298 " Host name, IP Address, or unix socket (must be an absolute path)\n");
282 printf(" %s\n", _("Optional port of which to connect")); 299 printf(" %s\n", "-P");
283 printf(" %s\n", "gf"); 300 printf(" %s\n", _("Optional port to connect to"));
301 printf(" %s\n", "-g");
284 printf(" %s\n", _("Field number in raw qstat output that contains game name")); 302 printf(" %s\n", _("Field number in raw qstat output that contains game name"));
285 printf(" %s\n", "-mf"); 303 printf(" %s\n", "-m");
286 printf(" %s\n", _("Field number in raw qstat output that contains map name")); 304 printf(" %s\n", _("Field number in raw qstat output that contains map name"));
287 printf(" %s\n", "-pf"); 305 printf(" %s\n", "-p");
288 printf(" %s\n", _("Field number in raw qstat output that contains ping time")); 306 printf(" %s\n", _("Field number in raw qstat output that contains ping time"));
289 307
290 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 308 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
291 309
292 printf("\n"); 310 printf("\n");
293 printf("%s\n", _("Notes:")); 311 printf("%s\n", _("Notes:"));
294 printf(" %s\n", _("This plugin uses the 'qstat' command, the popular game server status query tool.")); 312 printf(" %s\n",
295 printf(" %s\n", _("If you don't have the package installed, you will need to download it from")); 313 _("This plugin uses the 'qstat' command, the popular game server status query tool."));
314 printf(" %s\n",
315 _("If you don't have the package installed, you will need to download it from"));
296 printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin.")); 316 printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin."));
297 317
298 printf(UT_SUPPORT); 318 printf(UT_SUPPORT);
@@ -300,7 +320,8 @@ void print_help(void) {
300 320
301void print_usage(void) { 321void print_usage(void) {
302 printf("%s\n", _("Usage:")); 322 printf("%s\n", _("Usage:"));
303 printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G game-time] [-H hostname] <game> " 323 printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G "
324 "game-time] [-H hostname] <game> "
304 "<ip_address>\n", 325 "<ip_address>\n",
305 progname); 326 progname);
306} 327}
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 b39bccff..9907abc5 100644
--- a/plugins/check_hpjd.c
+++ b/plugins/check_hpjd.c
@@ -37,9 +37,10 @@ const char *email = "devel@monitoring-plugins.org";
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "netutils.h" 39#include "netutils.h"
40#include "states.h"
41#include "check_hpjd.d/config.h"
40 42
41#define DEFAULT_COMMUNITY "public" 43#define DEFAULT_COMMUNITY "public"
42#define DEFAULT_PORT "161"
43 44
44#define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1" 45#define HPJD_LINE_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.1"
45#define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2" 46#define HPJD_PAPER_STATUS ".1.3.6.1.4.1.11.2.3.9.1.1.2.2"
@@ -57,39 +58,15 @@ const char *email = "devel@monitoring-plugins.org";
57#define ONLINE 0 58#define ONLINE 0
58#define OFFLINE 1 59#define OFFLINE 1
59 60
60static int process_arguments(int /*argc*/, char ** /*argv*/); 61typedef struct {
61static int validate_arguments(void); 62 int errorcode;
63 check_hpjd_config config;
64} check_hpjd_config_wrapper;
65static check_hpjd_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
62static void print_help(void); 66static void print_help(void);
63void print_usage(void); 67void print_usage(void);
64 68
65static char *community = NULL;
66static char *address = NULL;
67static unsigned int port = 0;
68static int check_paper_out = 1;
69
70int main(int argc, char **argv) { 69int main(int argc, char **argv) {
71 char command_line[1024];
72 int result = STATE_UNKNOWN;
73 int line;
74 char input_buffer[MAX_INPUT_BUFFER];
75 char query_string[512];
76 char *errmsg;
77 char *temp_buffer;
78 int line_status = ONLINE;
79 int paper_status = 0;
80 int intervention_required = 0;
81 int peripheral_error = 0;
82 int paper_jam = 0;
83 int paper_out = 0;
84 int toner_low = 0;
85 int page_punt = 0;
86 int memory_out = 0;
87 int door_open = 0;
88 int paper_output = 0;
89 char display_message[MAX_INPUT_BUFFER];
90
91 errmsg = malloc(MAX_INPUT_BUFFER);
92
93 setlocale(LC_ALL, ""); 70 setlocale(LC_ALL, "");
94 bindtextdomain(PACKAGE, LOCALEDIR); 71 bindtextdomain(PACKAGE, LOCALEDIR);
95 textdomain(PACKAGE); 72 textdomain(PACKAGE);
@@ -97,17 +74,27 @@ int main(int argc, char **argv) {
97 /* Parse extra opts if any */ 74 /* Parse extra opts if any */
98 argv = np_extra_opts(&argc, argv, progname); 75 argv = np_extra_opts(&argc, argv, progname);
99 76
100 if (process_arguments(argc, argv) == ERROR) 77 check_hpjd_config_wrapper tmp_config = process_arguments(argc, argv);
78
79 if (tmp_config.errorcode == ERROR) {
101 usage4(_("Could not parse arguments")); 80 usage4(_("Could not parse arguments"));
81 }
82
83 const check_hpjd_config config = tmp_config.config;
102 84
85 char query_string[512];
103 /* removed ' 2>1' at end of command 10/27/1999 - EG */ 86 /* removed ' 2>1' at end of command 10/27/1999 - EG */
104 /* create the query string */ 87 /* create the query string */
105 sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", HPJD_LINE_STATUS, HPJD_PAPER_STATUS, 88 sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0",
106 HPJD_INTERVENTION_REQUIRED, HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW, 89 HPJD_LINE_STATUS, HPJD_PAPER_STATUS, HPJD_INTERVENTION_REQUIRED,
107 HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, HPJD_GD_STATUS_DISPLAY); 90 HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW,
91 HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT,
92 HPJD_GD_STATUS_DISPLAY);
108 93
109 /* get the command to run */ 94 /* get the command to run */
110 sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, community, address, port, query_string); 95 char command_line[1024];
96 sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community,
97 config.address, config.port, query_string);
111 98
112 /* run the command */ 99 /* run the command */
113 child_process = spopen(command_line); 100 child_process = spopen(command_line);
@@ -121,29 +108,41 @@ int main(int argc, char **argv) {
121 printf(_("Could not open stderr for %s\n"), command_line); 108 printf(_("Could not open stderr for %s\n"), command_line);
122 } 109 }
123 110
124 result = STATE_OK; 111 mp_state_enum result = STATE_OK;
125 112
126 line = 0; 113 int line_status = ONLINE;
127 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 114 int paper_status = 0;
115 int intervention_required = 0;
116 int peripheral_error = 0;
117 int paper_jam = 0;
118 int paper_out = 0;
119 int toner_low = 0;
120 int page_punt = 0;
121 int memory_out = 0;
122 int door_open = 0;
123 int paper_output = 0;
124 char display_message[MAX_INPUT_BUFFER];
128 125
126 char input_buffer[MAX_INPUT_BUFFER];
127 char *errmsg = malloc(MAX_INPUT_BUFFER);
128 int line = 0;
129
130 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
129 /* strip the newline character from the end of the input */ 131 /* strip the newline character from the end of the input */
130 if (input_buffer[strlen(input_buffer) - 1] == '\n') 132 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
131 input_buffer[strlen(input_buffer) - 1] = 0; 133 input_buffer[strlen(input_buffer) - 1] = 0;
134 }
132 135
133 line++; 136 line++;
134 137
135 temp_buffer = strtok(input_buffer, "="); 138 char *temp_buffer = strtok(input_buffer, "=");
136 temp_buffer = strtok(NULL, "="); 139 temp_buffer = strtok(NULL, "=");
137 140
138 if (temp_buffer == NULL && line < 13) { 141 if (temp_buffer == NULL && line < 13) {
139
140 result = STATE_UNKNOWN; 142 result = STATE_UNKNOWN;
141 strcpy(errmsg, input_buffer); 143 strcpy(errmsg, input_buffer);
142
143 } else { 144 } else {
144
145 switch (line) { 145 switch (line) {
146
147 case 1: /* 1st line should contain the line status */ 146 case 1: /* 1st line should contain the line status */
148 line_status = atoi(temp_buffer); 147 line_status = atoi(temp_buffer);
149 break; 148 break;
@@ -181,21 +180,24 @@ int main(int argc, char **argv) {
181 strcpy(display_message, temp_buffer + 1); 180 strcpy(display_message, temp_buffer + 1);
182 break; 181 break;
183 default: /* fold multiline message */ 182 default: /* fold multiline message */
184 strncat(display_message, input_buffer, sizeof(display_message) - strlen(display_message) - 1); 183 strncat(display_message, input_buffer,
184 sizeof(display_message) - strlen(display_message) - 1);
185 } 185 }
186 } 186 }
187 187
188 /* break out of the read loop if we encounter an error */ 188 /* break out of the read loop if we encounter an error */
189 if (result != STATE_OK) 189 if (result != STATE_OK) {
190 break; 190 break;
191 }
191 } 192 }
192 193
193 /* WARNING if output found on stderr */ 194 /* WARNING if output found on stderr */
194 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { 195 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
195 result = max_state(result, STATE_WARNING); 196 result = max_state(result, STATE_WARNING);
196 /* remove CRLF */ 197 /* remove CRLF */
197 if (input_buffer[strlen(input_buffer) - 1] == '\n') 198 if (input_buffer[strlen(input_buffer) - 1] == '\n') {
198 input_buffer[strlen(input_buffer) - 1] = 0; 199 input_buffer[strlen(input_buffer) - 1] = 0;
200 }
199 sprintf(errmsg, "%s", input_buffer); 201 sprintf(errmsg, "%s", input_buffer);
200 } 202 }
201 203
@@ -203,15 +205,15 @@ int main(int argc, char **argv) {
203 (void)fclose(child_stderr); 205 (void)fclose(child_stderr);
204 206
205 /* close the pipe */ 207 /* close the pipe */
206 if (spclose(child_process)) 208 if (spclose(child_process)) {
207 result = max_state(result, STATE_WARNING); 209 result = max_state(result, STATE_WARNING);
210 }
208 211
209 /* if there wasn't any output, display an error */ 212 /* if there wasn't any output, display an error */
210 if (line == 0) { 213 if (line == 0) {
211
212 /* might not be the problem, but most likely is. */ 214 /* might not be the problem, but most likely is. */
213 result = STATE_UNKNOWN; 215 result = STATE_UNKNOWN;
214 xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, address); 216 xasprintf(&errmsg, "%s : Timeout from host %s\n", errmsg, config.address);
215 } 217 }
216 218
217 /* if we had no read errors, check the printer status results... */ 219 /* if we had no read errors, check the printer status results... */
@@ -221,8 +223,9 @@ int main(int argc, char **argv) {
221 result = STATE_WARNING; 223 result = STATE_WARNING;
222 strcpy(errmsg, _("Paper Jam")); 224 strcpy(errmsg, _("Paper Jam"));
223 } else if (paper_out) { 225 } else if (paper_out) {
224 if (check_paper_out) 226 if (config.check_paper_out) {
225 result = STATE_WARNING; 227 result = STATE_WARNING;
228 }
226 strcpy(errmsg, _("Out of Paper")); 229 strcpy(errmsg, _("Out of Paper"));
227 } else if (line_status == OFFLINE) { 230 } else if (line_status == OFFLINE) {
228 if (strcmp(errmsg, "POWERSAVE ON") != 0) { 231 if (strcmp(errmsg, "POWERSAVE ON") != 0) {
@@ -256,29 +259,23 @@ int main(int argc, char **argv) {
256 } 259 }
257 } 260 }
258 261
259 if (result == STATE_OK) 262 if (result == STATE_OK) {
260 printf(_("Printer ok - (%s)\n"), display_message); 263 printf(_("Printer ok - (%s)\n"), display_message);
261 264 } else if (result == STATE_UNKNOWN) {
262 else if (result == STATE_UNKNOWN) {
263
264 printf("%s\n", errmsg); 265 printf("%s\n", errmsg);
265
266 /* if printer could not be reached, escalate to critical */ 266 /* if printer could not be reached, escalate to critical */
267 if (strstr(errmsg, "Timeout")) 267 if (strstr(errmsg, "Timeout")) {
268 result = STATE_CRITICAL; 268 result = STATE_CRITICAL;
269 } 269 }
270 270 } else if (result == STATE_WARNING) {
271 else if (result == STATE_WARNING)
272 printf("%s (%s)\n", errmsg, display_message); 271 printf("%s (%s)\n", errmsg, display_message);
272 }
273 273
274 return result; 274 exit(result);
275} 275}
276 276
277/* process command-line arguments */ 277/* process command-line arguments */
278int process_arguments(int argc, char **argv) { 278check_hpjd_config_wrapper process_arguments(int argc, char **argv) {
279 int c;
280
281 int option = 0;
282 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 279 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
283 {"community", required_argument, 0, 'C'}, 280 {"community", required_argument, 0, 'C'},
284 /* {"critical", required_argument,0,'c'}, */ 281 /* {"critical", required_argument,0,'c'}, */
@@ -288,34 +285,44 @@ int process_arguments(int argc, char **argv) {
288 {"help", no_argument, 0, 'h'}, 285 {"help", no_argument, 0, 'h'},
289 {0, 0, 0, 0}}; 286 {0, 0, 0, 0}};
290 287
291 if (argc < 2) 288 check_hpjd_config_wrapper result = {
292 return ERROR; 289 .errorcode = OK,
290 .config = check_hpjd_config_init(),
291 };
292
293 if (argc < 2) {
294 result.errorcode = ERROR;
295 return result;
296 }
293 297
294 while (1) { 298 int option = 0;
295 c = getopt_long(argc, argv, "+hVH:C:p:D", longopts, &option); 299 while (true) {
300 int option_index = getopt_long(argc, argv, "+hVH:C:p:D", longopts, &option);
296 301
297 if (c == -1 || c == EOF || c == 1) 302 if (option_index == -1 || option_index == EOF || option_index == 1) {
298 break; 303 break;
304 }
299 305
300 switch (c) { 306 switch (option_index) {
301 case 'H': /* hostname */ 307 case 'H': /* hostname */
302 if (is_host(optarg)) { 308 if (is_host(optarg)) {
303 address = strscpy(address, optarg); 309 result.config.address = strscpy(result.config.address, optarg);
304 } else { 310 } else {
305 usage2(_("Invalid hostname/address"), optarg); 311 usage2(_("Invalid hostname/address"), optarg);
306 } 312 }
307 break; 313 break;
308 case 'C': /* community */ 314 case 'C': /* community */
309 community = strscpy(community, optarg); 315 result.config.community = strscpy(result.config.community, optarg);
310 break; 316 break;
311 case 'p': 317 case 'p':
312 if (!is_intpos(optarg)) 318 if (!is_intpos(optarg)) {
313 usage2(_("Port must be a positive short integer"), optarg); 319 usage2(_("Port must be a positive short integer"), optarg);
314 else 320 } else {
315 port = atoi(optarg); 321 result.config.port = atoi(optarg);
322 }
316 break; 323 break;
317 case 'D': /* disable paper out check*/ 324 case 'D': /* disable paper out check*/
318 check_paper_out = 0; 325 result.config.check_paper_out = false;
319 break; 326 break;
320 case 'V': /* version */ 327 case 'V': /* version */
321 print_revision(progname, NP_VERSION); 328 print_revision(progname, NP_VERSION);
@@ -328,31 +335,26 @@ int process_arguments(int argc, char **argv) {
328 } 335 }
329 } 336 }
330 337
331 c = optind; 338 int c = optind;
332 if (address == NULL) { 339 if (result.config.address == NULL) {
333 if (is_host(argv[c])) { 340 if (is_host(argv[c])) {
334 address = argv[c++]; 341 result.config.address = argv[c++];
335 } else { 342 } else {
336 usage2(_("Invalid hostname/address"), argv[c]); 343 usage2(_("Invalid hostname/address"), argv[c]);
337 } 344 }
338 } 345 }
339 346
340 if (community == NULL) { 347 if (result.config.community == NULL) {
341 if (argv[c] != NULL) 348 if (argv[c] != NULL) {
342 community = argv[c]; 349 result.config.community = argv[c];
343 else 350 } else {
344 community = strdup(DEFAULT_COMMUNITY); 351 result.config.community = strdup(DEFAULT_COMMUNITY);
345 } 352 }
346
347 if (port == 0) {
348 port = atoi(DEFAULT_PORT);
349 } 353 }
350 354
351 return validate_arguments(); 355 return result;
352} 356}
353 357
354int validate_arguments(void) { return OK; }
355
356void print_help(void) { 358void print_help(void) {
357 print_revision(progname, NP_VERSION); 359 print_revision(progname, NP_VERSION);
358 360
diff --git a/plugins/check_hpjd.d/config.h b/plugins/check_hpjd.d/config.h
new file mode 100644
index 00000000..e36b7972
--- /dev/null
+++ b/plugins/check_hpjd.d/config.h
@@ -0,0 +1,25 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7#define DEFAULT_PORT "161"
8
9typedef struct {
10 char *address;
11 char *community;
12 unsigned int port;
13 bool check_paper_out;
14
15} check_hpjd_config;
16
17check_hpjd_config check_hpjd_config_init() {
18 check_hpjd_config tmp = {
19 .address = NULL,
20 .community = NULL,
21 .port = (unsigned int)atoi(DEFAULT_PORT),
22 .check_paper_out = true,
23 };
24 return tmp;
25}
diff --git a/plugins/check_http.c b/plugins/check_http.c
index baff682a..d264b95d 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -1,35 +1,35 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_http plugin 3 * Monitoring check_http plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_http plugin 10 * This file contains the check_http plugin
11* 11 *
12* This plugin tests the HTTP service on the specified host. It can test 12 * This plugin tests the HTTP service on the specified host. It can test
13* normal (http) and secure (https) servers, follow redirects, search for 13 * normal (http) and secure (https) servers, follow redirects, search for
14* strings and regular expressions, check connection times, and report on 14 * strings and regular expressions, check connection times, and report on
15* certificate expiration times. 15 * certificate expiration times.
16* 16 *
17* 17 *
18* This program is free software: you can redistribute it and/or modify 18 * This program is free software: you can redistribute it and/or modify
19* it under the terms of the GNU General Public License as published by 19 * it under the terms of the GNU General Public License as published by
20* the Free Software Foundation, either version 3 of the License, or 20 * the Free Software Foundation, either version 3 of the License, or
21* (at your option) any later version. 21 * (at your option) any later version.
22* 22 *
23* This program is distributed in the hope that it will be useful, 23 * This program is distributed in the hope that it will be useful,
24* but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26* GNU General Public License for more details. 26 * GNU General Public License for more details.
27* 27 *
28* You should have received a copy of the GNU General Public License 28 * You should have received a copy of the GNU General Public License
29* along with this program. If not, see <http://www.gnu.org/licenses/>. 29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30* 30 *
31* 31 *
32*****************************************************************************/ 32 *****************************************************************************/
33 33
34const char *progname = "check_http"; 34const char *progname = "check_http";
35const char *copyright = "1999-2024"; 35const char *copyright = "1999-2024";
@@ -41,7 +41,6 @@ const char *email = "devel@monitoring-plugins.org";
41#include "base64.h" 41#include "base64.h"
42#include "netutils.h" 42#include "netutils.h"
43#include "utils.h" 43#include "utils.h"
44#include "base64.h"
45#include <ctype.h> 44#include <ctype.h>
46 45
47#define STICKY_NONE 0 46#define STICKY_NONE 0
@@ -50,1346 +49,1394 @@ const char *email = "devel@monitoring-plugins.org";
50 49
51#define HTTP_EXPECT "HTTP/1." 50#define HTTP_EXPECT "HTTP/1."
52enum { 51enum {
53 MAX_IPV4_HOSTLENGTH = 255, 52 MAX_IPV4_HOSTLENGTH = 255,
54 HTTP_PORT = 80, 53 HTTP_PORT = 80,
55 HTTPS_PORT = 443, 54 HTTPS_PORT = 443,
56 MAX_PORT = 65535, 55 MAX_PORT = 65535,
57 DEFAULT_MAX_REDIRS = 15 56 DEFAULT_MAX_REDIRS = 15
58}; 57};
59 58
60#ifdef HAVE_SSL 59#ifdef HAVE_SSL
61bool check_cert = false; 60static bool check_cert = false;
62bool continue_after_check_cert = false; 61static bool continue_after_check_cert = false;
63int ssl_version = 0; 62static int ssl_version = 0;
64int days_till_exp_warn, days_till_exp_crit; 63static int days_till_exp_warn, days_till_exp_crit;
65char *randbuff; 64# define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
66X509 *server_cert; 65# define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
67# define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
68# define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
69#else /* ifndef HAVE_SSL */ 66#else /* ifndef HAVE_SSL */
70# define my_recv(buf, len) read(sd, buf, len) 67# define my_recv(buf, len) read(sd, buf, len)
71# define my_send(buf, len) send(sd, buf, len, 0) 68# define my_send(buf, len) send(sd, buf, len, 0)
72#endif /* HAVE_SSL */ 69#endif /* HAVE_SSL */
73bool no_body = false; 70static bool no_body = false;
74int maximum_age = -1; 71static int maximum_age = -1;
75 72
76enum { 73enum {
77 REGS = 2, 74 REGS = 2,
78 MAX_RE_SIZE = 1024 75 MAX_RE_SIZE = 1024
79}; 76};
80#include "regex.h" 77#include "regex.h"
81regex_t preg; 78static regex_t preg;
82regmatch_t pmatch[REGS]; 79static regmatch_t pmatch[REGS];
83char regexp[MAX_RE_SIZE]; 80static char regexp[MAX_RE_SIZE];
84char errbuf[MAX_INPUT_BUFFER]; 81static char errbuf[MAX_INPUT_BUFFER];
85int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; 82static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
86int errcode; 83static int errcode;
87int invert_regex = 0; 84static int invert_regex = 0;
88int state_regex = STATE_CRITICAL; 85static int state_regex = STATE_CRITICAL;
89 86
90struct timeval tv; 87static struct timeval tv;
91struct timeval tv_temp; 88static struct timeval tv_temp;
92 89
93#define HTTP_URL "/" 90#define HTTP_URL "/"
94#define CRLF "\r\n" 91#define CRLF "\r\n"
95 92
96bool specify_port = false; 93static bool specify_port = false;
97int server_port = HTTP_PORT; 94static int server_port = HTTP_PORT;
98int virtual_port = 0; 95static int virtual_port = 0;
99char server_port_text[6] = ""; 96static char server_type[6] = "http";
100char server_type[6] = "http"; 97static char *server_address;
101char *server_address; 98static char *host_name;
102char *host_name; 99static int host_name_length;
103int host_name_length; 100static char *server_url;
104char *server_url; 101static char *user_agent;
105char *user_agent; 102static int server_url_length;
106int server_url_length; 103static int server_expect_yn = 0;
107int server_expect_yn = 0; 104static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
108char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; 105static char header_expect[MAX_INPUT_BUFFER] = "";
109char header_expect[MAX_INPUT_BUFFER] = ""; 106static char string_expect[MAX_INPUT_BUFFER] = "";
110char string_expect[MAX_INPUT_BUFFER] = ""; 107static char *warning_thresholds = NULL;
111char *warning_thresholds = NULL; 108static char *critical_thresholds = NULL;
112char *critical_thresholds = NULL; 109static thresholds *thlds;
113thresholds *thlds; 110static char user_auth[MAX_INPUT_BUFFER] = "";
114char user_auth[MAX_INPUT_BUFFER] = ""; 111static char proxy_auth[MAX_INPUT_BUFFER] = "";
115char proxy_auth[MAX_INPUT_BUFFER] = ""; 112static bool display_html = false;
116bool display_html = false; 113static char **http_opt_headers;
117char **http_opt_headers; 114static int http_opt_headers_count = 0;
118int http_opt_headers_count = 0; 115static int onredirect = STATE_OK;
119int onredirect = STATE_OK; 116static int followsticky = STICKY_NONE;
120int followsticky = STICKY_NONE; 117static bool use_ssl = false;
121bool use_ssl = false; 118static bool use_sni = false;
122bool use_sni = false; 119static bool verbose = false;
123bool verbose = false; 120static bool show_extended_perfdata = false;
124bool show_extended_perfdata = false; 121static bool show_body = false;
125bool show_body = false; 122static int sd;
126int sd; 123static int min_page_len = 0;
127int min_page_len = 0; 124static int max_page_len = 0;
128int max_page_len = 0; 125static int redir_depth = 0;
129int redir_depth = 0; 126static int max_depth = DEFAULT_MAX_REDIRS;
130int max_depth = DEFAULT_MAX_REDIRS; 127static char *http_method;
131char *http_method; 128static char *http_method_proxy;
132char *http_method_proxy; 129static char *http_post_data;
133char *http_post_data; 130static char *http_content_type;
134char *http_content_type; 131static char buffer[MAX_INPUT_BUFFER];
135char buffer[MAX_INPUT_BUFFER]; 132static char *client_cert = NULL;
136char *client_cert = NULL; 133static char *client_privkey = NULL;
137char *client_privkey = NULL;
138 134
139// Forward function declarations 135// Forward function declarations
140bool process_arguments (int, char **); 136static bool process_arguments(int /*argc*/, char ** /*argv*/);
141int check_http (void); 137static int check_http(void);
142void redir (char *pos, char *status_line); 138static void redir(char *pos, char *status_line);
143bool server_type_check(const char *type); 139static bool server_type_check(const char *type);
144int server_port_check(int ssl_flag); 140static int server_port_check(int ssl_flag);
145char *perfd_time (double microsec); 141static char *perfd_time(double elapsed_time);
146char *perfd_time_connect (double microsec); 142static char *perfd_time_connect(double elapsed_time_connect);
147char *perfd_time_ssl (double microsec); 143static char *perfd_time_ssl(double elapsed_time_ssl);
148char *perfd_time_firstbyte (double microsec); 144static char *perfd_time_firstbyte(double elapsed_time_firstbyte);
149char *perfd_time_headers (double microsec); 145static char *perfd_time_headers(double elapsed_time_headers);
150char *perfd_time_transfer (double microsec); 146static char *perfd_time_transfer(double elapsed_time_transfer);
151char *perfd_size (int page_len); 147static char *perfd_size(int page_len);
152void print_help (void); 148void print_help(void);
153void print_usage (void); 149void print_usage(void);
154char *unchunk_content(const char *content); 150static char *unchunk_content(const char *content);
155 151
156int 152int main(int argc, char **argv) {
157main (int argc, char **argv) 153 int result = STATE_UNKNOWN;
158{ 154
159 int result = STATE_UNKNOWN; 155 setlocale(LC_ALL, "");
160 156 bindtextdomain(PACKAGE, LOCALEDIR);
161 setlocale (LC_ALL, ""); 157 textdomain(PACKAGE);
162 bindtextdomain (PACKAGE, LOCALEDIR); 158
163 textdomain (PACKAGE); 159 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
164 160 server_url = strdup(HTTP_URL);
165 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */ 161 server_url_length = strlen(server_url);
166 server_url = strdup(HTTP_URL); 162 xasprintf(&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)", NP_VERSION,
167 server_url_length = strlen(server_url); 163 VERSION);
168 xasprintf (&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)", 164
169 NP_VERSION, VERSION); 165 /* Parse extra opts if any */
170 166 argv = np_extra_opts(&argc, argv, progname);
171 /* Parse extra opts if any */ 167
172 argv=np_extra_opts (&argc, argv, progname); 168 if (!process_arguments(argc, argv)) {
173 169 usage4(_("Could not parse arguments"));
174 if (process_arguments (argc, argv) == false) 170 }
175 usage4 (_("Could not parse arguments")); 171
176 172 if (display_html) {
177 if (display_html == true) 173 printf("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", use_ssl ? "https" : "http",
178 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", 174 host_name ? host_name : server_address, server_port, server_url);
179 use_ssl ? "https" : "http", host_name ? host_name : server_address, 175 }
180 server_port, server_url); 176
181 177 /* initialize alarm signal handling, set socket timeout, start timer */
182 /* initialize alarm signal handling, set socket timeout, start timer */ 178 (void)signal(SIGALRM, socket_timeout_alarm_handler);
183 (void) signal (SIGALRM, socket_timeout_alarm_handler); 179 (void)alarm(socket_timeout);
184 (void) alarm (socket_timeout); 180 gettimeofday(&tv, NULL);
185 gettimeofday (&tv, NULL); 181
186 182 result = check_http();
187 result = check_http (); 183 return result;
188 return result;
189} 184}
190 185
191/* check whether a file exists */ 186/* check whether a file exists */
192void 187void test_file(char *path) {
193test_file (char *path) 188 if (access(path, R_OK) == 0) {
194{ 189 return;
195 if (access(path, R_OK) == 0) 190 }
196 return; 191 usage2(_("file does not exist or is not readable"), path);
197 usage2 (_("file does not exist or is not readable"), path);
198} 192}
199 193
200/* 194/*
201 * process command-line arguments 195 * process command-line arguments
202 * returns true on success, false otherwise 196 * returns true on success, false otherwise
203 */ 197 */
204bool process_arguments (int argc, char **argv) 198bool process_arguments(int argc, char **argv) {
205{ 199 int c = 1;
206 int c = 1; 200 char *p;
207 char *p; 201 char *temp;
208 char *temp; 202
209 203 enum {
210 enum { 204 INVERT_REGEX = CHAR_MAX + 1,
211 INVERT_REGEX = CHAR_MAX + 1, 205 SNI_OPTION,
212 SNI_OPTION, 206 MAX_REDIRS_OPTION,
213 MAX_REDIRS_OPTION, 207 CONTINUE_AFTER_CHECK_CERT,
214 CONTINUE_AFTER_CHECK_CERT, 208 STATE_REGEX
215 STATE_REGEX 209 };
216 }; 210
217 211 int option = 0;
218 int option = 0; 212 static struct option longopts[] = {
219 static struct option longopts[] = { 213 STD_LONG_OPTS,
220 STD_LONG_OPTS, 214 {"link", no_argument, 0, 'L'},
221 {"link", no_argument, 0, 'L'}, 215 {"nohtml", no_argument, 0, 'n'},
222 {"nohtml", no_argument, 0, 'n'}, 216 {"ssl", optional_argument, 0, 'S'},
223 {"ssl", optional_argument, 0, 'S'}, 217 {"sni", no_argument, 0, SNI_OPTION},
224 {"sni", no_argument, 0, SNI_OPTION}, 218 {"post", required_argument, 0, 'P'},
225 {"post", required_argument, 0, 'P'}, 219 {"method", required_argument, 0, 'j'},
226 {"method", required_argument, 0, 'j'}, 220 {"IP-address", required_argument, 0, 'I'},
227 {"IP-address", required_argument, 0, 'I'}, 221 {"url", required_argument, 0, 'u'},
228 {"url", required_argument, 0, 'u'}, 222 {"port", required_argument, 0, 'p'},
229 {"port", required_argument, 0, 'p'}, 223 {"authorization", required_argument, 0, 'a'},
230 {"authorization", required_argument, 0, 'a'}, 224 {"proxy-authorization", required_argument, 0, 'b'},
231 {"proxy-authorization", required_argument, 0, 'b'}, 225 {"header-string", required_argument, 0, 'd'},
232 {"header-string", required_argument, 0, 'd'}, 226 {"string", required_argument, 0, 's'},
233 {"string", required_argument, 0, 's'}, 227 {"expect", required_argument, 0, 'e'},
234 {"expect", required_argument, 0, 'e'}, 228 {"regex", required_argument, 0, 'r'},
235 {"regex", required_argument, 0, 'r'}, 229 {"ereg", required_argument, 0, 'r'},
236 {"ereg", required_argument, 0, 'r'}, 230 {"eregi", required_argument, 0, 'R'},
237 {"eregi", required_argument, 0, 'R'}, 231 {"linespan", no_argument, 0, 'l'},
238 {"linespan", no_argument, 0, 'l'}, 232 {"onredirect", required_argument, 0, 'f'},
239 {"onredirect", required_argument, 0, 'f'}, 233 {"certificate", required_argument, 0, 'C'},
240 {"certificate", required_argument, 0, 'C'}, 234 {"client-cert", required_argument, 0, 'J'},
241 {"client-cert", required_argument, 0, 'J'}, 235 {"private-key", required_argument, 0, 'K'},
242 {"private-key", required_argument, 0, 'K'}, 236 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
243 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, 237 {"useragent", required_argument, 0, 'A'},
244 {"useragent", required_argument, 0, 'A'}, 238 {"header", required_argument, 0, 'k'},
245 {"header", required_argument, 0, 'k'}, 239 {"no-body", no_argument, 0, 'N'},
246 {"no-body", no_argument, 0, 'N'}, 240 {"max-age", required_argument, 0, 'M'},
247 {"max-age", required_argument, 0, 'M'}, 241 {"content-type", required_argument, 0, 'T'},
248 {"content-type", required_argument, 0, 'T'}, 242 {"pagesize", required_argument, 0, 'm'},
249 {"pagesize", required_argument, 0, 'm'}, 243 {"invert-regex", no_argument, NULL, INVERT_REGEX},
250 {"invert-regex", no_argument, NULL, INVERT_REGEX}, 244 {"state-regex", required_argument, 0, STATE_REGEX},
251 {"state-regex", required_argument, 0, STATE_REGEX}, 245 {"use-ipv4", no_argument, 0, '4'},
252 {"use-ipv4", no_argument, 0, '4'}, 246 {"use-ipv6", no_argument, 0, '6'},
253 {"use-ipv6", no_argument, 0, '6'}, 247 {"extended-perfdata", no_argument, 0, 'E'},
254 {"extended-perfdata", no_argument, 0, 'E'}, 248 {"show-body", no_argument, 0, 'B'},
255 {"show-body", no_argument, 0, 'B'}, 249 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
256 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, 250 {0, 0, 0, 0}};
257 {0, 0, 0, 0} 251
258 }; 252 if (argc < 2) {
259 253 return false;
260 if (argc < 2) 254 }
261 return false; 255
262 256 for (c = 1; c < argc; c++) {
263 for (c = 1; c < argc; c++) { 257 if (strcmp("-to", argv[c]) == 0) {
264 if (strcmp ("-to", argv[c]) == 0) 258 strcpy(argv[c], "-t");
265 strcpy (argv[c], "-t"); 259 }
266 if (strcmp ("-hn", argv[c]) == 0) 260 if (strcmp("-hn", argv[c]) == 0) {
267 strcpy (argv[c], "-H"); 261 strcpy(argv[c], "-H");
268 if (strcmp ("-wt", argv[c]) == 0) 262 }
269 strcpy (argv[c], "-w"); 263 if (strcmp("-wt", argv[c]) == 0) {
270 if (strcmp ("-ct", argv[c]) == 0) 264 strcpy(argv[c], "-w");
271 strcpy (argv[c], "-c"); 265 }
272 if (strcmp ("-nohtml", argv[c]) == 0) 266 if (strcmp("-ct", argv[c]) == 0) {
273 strcpy (argv[c], "-n"); 267 strcpy(argv[c], "-c");
274 } 268 }
275 269 if (strcmp("-nohtml", argv[c]) == 0) {
276 while (1) { 270 strcpy(argv[c], "-n");
277 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB", longopts, &option); 271 }
278 if (c == -1 || c == EOF) 272 }
279 break; 273
280 274 while (1) {
281 switch (c) { 275 c = getopt_long(argc, argv,
282 case '?': /* usage */ 276 "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB",
283 usage5 (); 277 longopts, &option);
284 break; 278 if (c == -1 || c == EOF) {
285 case 'h': /* help */ 279 break;
286 print_help (); 280 }
287 exit (STATE_UNKNOWN); 281
288 break; 282 switch (c) {
289 case 'V': /* version */ 283 case '?': /* usage */
290 print_revision (progname, NP_VERSION); 284 usage5();
291 exit (STATE_UNKNOWN); 285 break;
292 break; 286 case 'h': /* help */
293 case 't': /* timeout period */ 287 print_help();
294 if (!is_intnonneg (optarg)) 288 exit(STATE_UNKNOWN);
295 usage2 (_("Timeout interval must be a positive integer"), optarg); 289 break;
296 else 290 case 'V': /* version */
297 socket_timeout = atoi (optarg); 291 print_revision(progname, NP_VERSION);
298 break; 292 exit(STATE_UNKNOWN);
299 case 'c': /* critical time threshold */ 293 break;
300 critical_thresholds = optarg; 294 case 't': /* timeout period */
301 break; 295 if (!is_intnonneg(optarg)) {
302 case 'w': /* warning time threshold */ 296 usage2(_("Timeout interval must be a positive integer"), optarg);
303 warning_thresholds = optarg; 297 } else {
304 break; 298 socket_timeout = atoi(optarg);
305 case 'A': /* User Agent String */ 299 }
306 xasprintf (&user_agent, "User-Agent: %s", optarg); 300 break;
307 break; 301 case 'c': /* critical time threshold */
308 case 'k': /* Additional headers */ 302 critical_thresholds = optarg;
309 if (http_opt_headers_count == 0) 303 break;
310 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count)); 304 case 'w': /* warning time threshold */
311 else 305 warning_thresholds = optarg;
312 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count)); 306 break;
313 http_opt_headers[http_opt_headers_count - 1] = optarg; 307 case 'A': /* User Agent String */
314 /* xasprintf (&http_opt_headers, "%s", optarg); */ 308 xasprintf(&user_agent, "User-Agent: %s", optarg);
315 break; 309 break;
316 case 'L': /* show html link */ 310 case 'k': /* Additional headers */
317 display_html = true; 311 if (http_opt_headers_count == 0) {
318 break; 312 http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count));
319 case 'n': /* do not show html link */ 313 } else {
320 display_html = false; 314 http_opt_headers =
321 break; 315 realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count));
322 case 'C': /* Check SSL cert validity */ 316 }
317 http_opt_headers[http_opt_headers_count - 1] = optarg;
318 /* xasprintf (&http_opt_headers, "%s", optarg); */
319 break;
320 case 'L': /* show html link */
321 display_html = true;
322 break;
323 case 'n': /* do not show html link */
324 display_html = false;
325 break;
326 case 'C': /* Check SSL cert validity */
323#ifdef HAVE_SSL 327#ifdef HAVE_SSL
324 if ((temp=strchr(optarg,','))!=NULL) { 328 if ((temp = strchr(optarg, ',')) != NULL) {
325 *temp='\0'; 329 *temp = '\0';
326 if (!is_intnonneg (optarg)) 330 if (!is_intnonneg(optarg)) {
327 usage2 (_("Invalid certificate expiration period"), optarg); 331 usage2(_("Invalid certificate expiration period"), optarg);
328 days_till_exp_warn = atoi(optarg); 332 }
329 *temp=','; 333 days_till_exp_warn = atoi(optarg);
330 temp++; 334 *temp = ',';
331 if (!is_intnonneg (temp)) 335 temp++;
332 usage2 (_("Invalid certificate expiration period"), temp); 336 if (!is_intnonneg(temp)) {
333 days_till_exp_crit = atoi (temp); 337 usage2(_("Invalid certificate expiration period"), temp);
334 } 338 }
335 else { 339 days_till_exp_crit = atoi(temp);
336 days_till_exp_crit=0; 340 } else {
337 if (!is_intnonneg (optarg)) 341 days_till_exp_crit = 0;
338 usage2 (_("Invalid certificate expiration period"), optarg); 342 if (!is_intnonneg(optarg)) {
339 days_till_exp_warn = atoi (optarg); 343 usage2(_("Invalid certificate expiration period"), optarg);
340 } 344 }
341 check_cert = true; 345 days_till_exp_warn = atoi(optarg);
342 goto enable_ssl; 346 }
347 check_cert = true;
348 goto enable_ssl;
343#endif 349#endif
344 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ 350 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
345#ifdef HAVE_SSL 351#ifdef HAVE_SSL
346 continue_after_check_cert = true; 352 continue_after_check_cert = true;
347 break; 353 break;
348#endif 354#endif
349 case 'J': /* use client certificate */ 355 case 'J': /* use client certificate */
350#ifdef HAVE_SSL 356#ifdef HAVE_SSL
351 test_file(optarg); 357 test_file(optarg);
352 client_cert = optarg; 358 client_cert = optarg;
353 goto enable_ssl; 359 goto enable_ssl;
354#endif 360#endif
355 case 'K': /* use client private key */ 361 case 'K': /* use client private key */
356#ifdef HAVE_SSL 362#ifdef HAVE_SSL
357 test_file(optarg); 363 test_file(optarg);
358 client_privkey = optarg; 364 client_privkey = optarg;
359 goto enable_ssl; 365 goto enable_ssl;
360#endif 366#endif
361 case 'S': /* use SSL */ 367 case 'S': /* use SSL */
362#ifdef HAVE_SSL 368#ifdef HAVE_SSL
363 enable_ssl: 369 enable_ssl:
364 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple 370 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps
365 parameters, like -S and -C combinations */ 371 when we include multiple parameters, like -S and -C combinations */
366 use_ssl = true; 372 use_ssl = true;
367 if (c=='S' && optarg != NULL) { 373 if (c == 'S' && optarg != NULL) {
368 int got_plus = strchr(optarg, '+') != NULL; 374 int got_plus = strchr(optarg, '+') != NULL;
369 375
370 if (!strncmp (optarg, "1.2", 3)) 376 if (!strncmp(optarg, "1.2", 3)) {
371 ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2; 377 ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2;
372 else if (!strncmp (optarg, "1.1", 3)) 378 } else if (!strncmp(optarg, "1.1", 3)) {
373 ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1; 379 ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1;
374 else if (optarg[0] == '1') 380 } else if (optarg[0] == '1') {
375 ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1; 381 ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1;
376 else if (optarg[0] == '3') 382 } else if (optarg[0] == '3') {
377 ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3; 383 ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3;
378 else if (optarg[0] == '2') 384 } else if (optarg[0] == '2') {
379 ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2; 385 ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2;
380 else 386 } else {
381 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)")); 387 usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with "
382 } 388 "optional '+' suffix)"));
383 if (specify_port == false) 389 }
384 server_port = HTTPS_PORT; 390 }
391 if (!specify_port) {
392 server_port = HTTPS_PORT;
393 }
385#else 394#else
386 /* -C -J and -K fall through to here without SSL */ 395 /* -C -J and -K fall through to here without SSL */
387 usage4 (_("Invalid option - SSL is not available")); 396 usage4(_("Invalid option - SSL is not available"));
388#endif 397#endif
389 break; 398 break;
390 case SNI_OPTION: 399 case SNI_OPTION:
391 use_sni = true; 400 use_sni = true;
392 break; 401 break;
393 case MAX_REDIRS_OPTION: 402 case MAX_REDIRS_OPTION:
394 if (!is_intnonneg (optarg)) 403 if (!is_intnonneg(optarg)) {
395 usage2 (_("Invalid max_redirs count"), optarg); 404 usage2(_("Invalid max_redirs count"), optarg);
396 else { 405 } else {
397 max_depth = atoi (optarg); 406 max_depth = atoi(optarg);
398 } 407 }
399 break; 408 break;
400 case 'f': /* onredirect */ 409 case 'f': /* onredirect */
401 if (!strcmp (optarg, "stickyport")) 410 if (!strcmp(optarg, "stickyport")) {
402 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT; 411 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST | STICKY_PORT;
403 else if (!strcmp (optarg, "sticky")) 412 } else if (!strcmp(optarg, "sticky")) {
404 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST; 413 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
405 else if (!strcmp (optarg, "follow")) 414 } else if (!strcmp(optarg, "follow")) {
406 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE; 415 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
407 else if (!strcmp (optarg, "unknown")) 416 } else if (!strcmp(optarg, "unknown")) {
408 onredirect = STATE_UNKNOWN; 417 onredirect = STATE_UNKNOWN;
409 else if (!strcmp (optarg, "ok")) 418 } else if (!strcmp(optarg, "ok")) {
410 onredirect = STATE_OK; 419 onredirect = STATE_OK;
411 else if (!strcmp (optarg, "warning")) 420 } else if (!strcmp(optarg, "warning")) {
412 onredirect = STATE_WARNING; 421 onredirect = STATE_WARNING;
413 else if (!strcmp (optarg, "critical")) 422 } else if (!strcmp(optarg, "critical")) {
414 onredirect = STATE_CRITICAL; 423 onredirect = STATE_CRITICAL;
415 else usage2 (_("Invalid onredirect option"), optarg); 424 } else {
416 if (verbose) 425 usage2(_("Invalid onredirect option"), optarg);
417 printf(_("option f:%d \n"), onredirect); 426 }
418 break; 427 if (verbose) {
419 /* Note: H, I, and u must be malloc'd or will fail on redirects */ 428 printf(_("option f:%d \n"), onredirect);
420 case 'H': /* Host Name (virtual host) */ 429 }
421 host_name = strdup (optarg); 430 break;
422 if (host_name[0] == '[') { 431 /* Note: H, I, and u must be malloc'd or will fail on redirects */
423 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */ 432 case 'H': /* Host Name (virtual host) */
424 virtual_port = atoi (p + 2); 433 host_name = strdup(optarg);
425 /* cut off the port */ 434 if (host_name[0] == '[') {
426 host_name_length = strlen (host_name) - strlen (p) - 1; 435 if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */
427 free (host_name); 436 virtual_port = atoi(p + 2);
428 host_name = strndup (optarg, host_name_length); 437 /* cut off the port */
429 if (specify_port == false) 438 host_name_length = strlen(host_name) - strlen(p) - 1;
430 server_port = virtual_port; 439 free(host_name);
431 } 440 host_name = strndup(optarg, host_name_length);
432 } else if ((p = strchr (host_name, ':')) != NULL 441 if (!specify_port) {
433 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */ 442 server_port = virtual_port;
434 virtual_port = atoi (p); 443 }
435 /* cut off the port */ 444 }
436 host_name_length = strlen (host_name) - strlen (p) - 1; 445 } else if ((p = strchr(host_name, ':')) != NULL &&
437 free (host_name); 446 strchr(++p, ':') == NULL) { /* IPv4:port or host:port */
438 host_name = strndup (optarg, host_name_length); 447 virtual_port = atoi(p);
439 if (specify_port == false) 448 /* cut off the port */
440 server_port = virtual_port; 449 host_name_length = strlen(host_name) - strlen(p) - 1;
441 } 450 free(host_name);
442 break; 451 host_name = strndup(optarg, host_name_length);
443 case 'I': /* Server IP-address */ 452 if (!specify_port) {
444 server_address = strdup (optarg); 453 server_port = virtual_port;
445 break; 454 }
446 case 'u': /* URL path */ 455 }
447 server_url = strdup (optarg); 456 break;
448 server_url_length = strlen (server_url); 457 case 'I': /* Server IP-address */
449 break; 458 server_address = strdup(optarg);
450 case 'p': /* Server port */ 459 break;
451 if (!is_intnonneg (optarg)) 460 case 'u': /* URL path */
452 usage2 (_("Invalid port number"), optarg); 461 server_url = strdup(optarg);
453 else { 462 server_url_length = strlen(server_url);
454 server_port = atoi (optarg); 463 break;
455 specify_port = true; 464 case 'p': /* Server port */
456 } 465 if (!is_intnonneg(optarg)) {
457 break; 466 usage2(_("Invalid port number"), optarg);
458 case 'a': /* authorization info */ 467 } else {
459 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1); 468 server_port = atoi(optarg);
460 user_auth[MAX_INPUT_BUFFER - 1] = 0; 469 specify_port = true;
461 break; 470 }
462 case 'b': /* proxy-authorization info */ 471 break;
463 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 472 case 'a': /* authorization info */
464 proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 473 strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1);
465 break; 474 user_auth[MAX_INPUT_BUFFER - 1] = 0;
466 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ 475 break;
467 if (! http_post_data) 476 case 'b': /* proxy-authorization info */
468 http_post_data = strdup (optarg); 477 strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
469 if (! http_method) 478 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
470 http_method = strdup("POST"); 479 break;
471 break; 480 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
472 case 'j': /* Set HTTP method */ 481 if (!http_post_data) {
473 if (http_method) 482 http_post_data = strdup(optarg);
474 free(http_method); 483 }
475 http_method = strdup (optarg); 484 if (!http_method) {
476 char *tmp; 485 http_method = strdup("POST");
477 if ((tmp = strstr(http_method, ":")) != NULL) { 486 }
478 tmp[0] = '\0'; // set the ":" in the middle to 0 487 break;
479 http_method_proxy = ++tmp; // this points to the second part 488 case 'j': /* Set HTTP method */
480 } 489 if (http_method) {
481 break; 490 free(http_method);
482 case 'd': /* string or substring */ 491 }
483 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1); 492 http_method = strdup(optarg);
484 header_expect[MAX_INPUT_BUFFER - 1] = 0; 493 char *tmp;
485 break; 494 if ((tmp = strstr(http_method, ":")) != NULL) {
486 case 's': /* string or substring */ 495 tmp[0] = '\0'; // set the ":" in the middle to 0
487 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1); 496 http_method_proxy = ++tmp; // this points to the second part
488 string_expect[MAX_INPUT_BUFFER - 1] = 0; 497 }
489 break; 498 break;
490 case 'e': /* string or substring */ 499 case 'd': /* string or substring */
491 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1); 500 strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1);
492 server_expect[MAX_INPUT_BUFFER - 1] = 0; 501 header_expect[MAX_INPUT_BUFFER - 1] = 0;
493 server_expect_yn = 1; 502 break;
494 break; 503 case 's': /* string or substring */
495 case 'T': /* Content-type */ 504 strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1);
496 xasprintf (&http_content_type, "%s", optarg); 505 string_expect[MAX_INPUT_BUFFER - 1] = 0;
497 break; 506 break;
498 case 'l': /* linespan */ 507 case 'e': /* string or substring */
499 cflags &= ~REG_NEWLINE; 508 strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1);
500 break; 509 server_expect[MAX_INPUT_BUFFER - 1] = 0;
501 case 'R': /* regex */ 510 server_expect_yn = 1;
502 cflags |= REG_ICASE; 511 break;
512 case 'T': /* Content-type */
513 xasprintf(&http_content_type, "%s", optarg);
514 break;
515 case 'l': /* linespan */
516 cflags &= ~REG_NEWLINE;
517 break;
518 case 'R': /* regex */
519 cflags |= REG_ICASE;
503 // fall through 520 // fall through
504 case 'r': /* regex */ 521 case 'r': /* regex */
505 strncpy (regexp, optarg, MAX_RE_SIZE - 1); 522 strncpy(regexp, optarg, MAX_RE_SIZE - 1);
506 regexp[MAX_RE_SIZE - 1] = 0; 523 regexp[MAX_RE_SIZE - 1] = 0;
507 errcode = regcomp (&preg, regexp, cflags); 524 errcode = regcomp(&preg, regexp, cflags);
508 if (errcode != 0) { 525 if (errcode != 0) {
509 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 526 (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
510 printf (_("Could Not Compile Regular Expression: %s"), errbuf); 527 printf(_("Could Not Compile Regular Expression: %s"), errbuf);
511 return false; 528 return false;
512 } 529 }
513 break; 530 break;
514 case INVERT_REGEX: 531 case INVERT_REGEX:
515 invert_regex = 1; 532 invert_regex = 1;
516 break; 533 break;
517 case STATE_REGEX: 534 case STATE_REGEX:
518 if (!strcmp (optarg, "critical")) 535 if (!strcmp(optarg, "critical")) {
519 state_regex = STATE_CRITICAL; 536 state_regex = STATE_CRITICAL;
520 else if (!strcmp (optarg, "warning")) 537 } else if (!strcmp(optarg, "warning")) {
521 state_regex = STATE_WARNING; 538 state_regex = STATE_WARNING;
522 else usage2 (_("Invalid state-regex option"), optarg); 539 } else {
523 break; 540 usage2(_("Invalid state-regex option"), optarg);
524 case '4': 541 }
525 address_family = AF_INET; 542 break;
526 break; 543 case '4':
527 case '6': 544 address_family = AF_INET;
545 break;
546 case '6':
528#ifdef USE_IPV6 547#ifdef USE_IPV6
529 address_family = AF_INET6; 548 address_family = AF_INET6;
530#else 549#else
531 usage4 (_("IPv6 support not available")); 550 usage4(_("IPv6 support not available"));
532#endif 551#endif
533 break; 552 break;
534 case 'v': /* verbose */ 553 case 'v': /* verbose */
535 verbose = true; 554 verbose = true;
536 break; 555 break;
537 case 'm': /* min_page_length */ 556 case 'm': /* min_page_length */
538 { 557 {
539 char *tmp; 558 char *tmp;
540 if (strchr(optarg, ':') != (char *)NULL) { 559 if (strchr(optarg, ':') != (char *)NULL) {
541 /* range, so get two values, min:max */ 560 /* range, so get two values, min:max */
542 tmp = strtok(optarg, ":"); 561 tmp = strtok(optarg, ":");
543 if (tmp == NULL) { 562 if (tmp == NULL) {
544 printf("Bad format: try \"-m min:max\"\n"); 563 printf("Bad format: try \"-m min:max\"\n");
545 exit (STATE_WARNING); 564 exit(STATE_WARNING);
546 } else 565 } else {
547 min_page_len = atoi(tmp); 566 min_page_len = atoi(tmp);
548 567 }
549 tmp = strtok(NULL, ":"); 568
550 if (tmp == NULL) { 569 tmp = strtok(NULL, ":");
551 printf("Bad format: try \"-m min:max\"\n"); 570 if (tmp == NULL) {
552 exit (STATE_WARNING); 571 printf("Bad format: try \"-m min:max\"\n");
553 } else 572 exit(STATE_WARNING);
554 max_page_len = atoi(tmp); 573 } else {
555 } else 574 max_page_len = atoi(tmp);
556 min_page_len = atoi (optarg); 575 }
557 break; 576 } else {
558 } 577 min_page_len = atoi(optarg);
559 case 'N': /* no-body */ 578 }
560 no_body = true; 579 break;
561 break; 580 }
562 case 'M': /* max-age */ 581 case 'N': /* no-body */
563 { 582 no_body = true;
564 int L = strlen(optarg); 583 break;
565 if (L && optarg[L-1] == 'm') 584 case 'M': /* max-age */
566 maximum_age = atoi (optarg) * 60; 585 {
567 else if (L && optarg[L-1] == 'h') 586 int L = strlen(optarg);
568 maximum_age = atoi (optarg) * 60 * 60; 587 if (L && optarg[L - 1] == 'm') {
569 else if (L && optarg[L-1] == 'd') 588 maximum_age = atoi(optarg) * 60;
570 maximum_age = atoi (optarg) * 60 * 60 * 24; 589 } else if (L && optarg[L - 1] == 'h') {
571 else if (L && (optarg[L-1] == 's' || 590 maximum_age = atoi(optarg) * 60 * 60;
572 isdigit (optarg[L-1]))) 591 } else if (L && optarg[L - 1] == 'd') {
573 maximum_age = atoi (optarg); 592 maximum_age = atoi(optarg) * 60 * 60 * 24;
574 else { 593 } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) {
575 fprintf (stderr, "unparsable max-age: %s\n", optarg); 594 maximum_age = atoi(optarg);
576 exit (STATE_WARNING); 595 } else {
577 } 596 fprintf(stderr, "unparsable max-age: %s\n", optarg);
578 } 597 exit(STATE_WARNING);
579 break; 598 }
580 case 'E': /* show extended perfdata */ 599 } break;
581 show_extended_perfdata = true; 600 case 'E': /* show extended perfdata */
582 break; 601 show_extended_perfdata = true;
583 case 'B': /* print body content after status line */ 602 break;
584 show_body = true; 603 case 'B': /* print body content after status line */
585 break; 604 show_body = true;
586 } 605 break;
587 } 606 }
588 607 }
589 c = optind; 608
590 609 c = optind;
591 if (server_address == NULL && c < argc) 610
592 server_address = strdup (argv[c++]); 611 if (server_address == NULL && c < argc) {
593 612 server_address = strdup(argv[c++]);
594 if (host_name == NULL && c < argc) 613 }
595 host_name = strdup (argv[c++]);
596
597 if (server_address == NULL) {
598 if (host_name == NULL)
599 usage4 (_("You must specify a server address or host name"));
600 else
601 server_address = strdup (host_name);
602 }
603
604 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
605
606 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
607 socket_timeout = (int)thlds->critical->end + 1;
608
609 if (http_method == NULL)
610 http_method = strdup ("GET");
611
612 if (http_method_proxy == NULL)
613 http_method_proxy = strdup ("GET");
614
615 if (client_cert && !client_privkey)
616 usage4 (_("If you use a client certificate you must also specify a private key file"));
617
618 if (virtual_port == 0)
619 virtual_port = server_port;
620
621 return true;
622}
623 614
615 if (host_name == NULL && c < argc) {
616 host_name = strdup(argv[c++]);
617 }
618
619 if (server_address == NULL) {
620 if (host_name == NULL) {
621 usage4(_("You must specify a server address or host name"));
622 } else {
623 server_address = strdup(host_name);
624 }
625 }
624 626
627 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
628
629 if (critical_thresholds && thlds->critical->end > (double)socket_timeout) {
630 socket_timeout = (int)thlds->critical->end + 1;
631 }
632
633 if (http_method == NULL) {
634 http_method = strdup("GET");
635 }
636
637 if (http_method_proxy == NULL) {
638 http_method_proxy = strdup("GET");
639 }
640
641 if (client_cert && !client_privkey) {
642 usage4(_("If you use a client certificate you must also specify a private key file"));
643 }
644
645 if (virtual_port == 0) {
646 virtual_port = server_port;
647 }
648
649 return true;
650}
625 651
626/* Returns 1 if we're done processing the document body; 0 to keep going */ 652/* Returns 1 if we're done processing the document body; 0 to keep going */
627static int 653static int document_headers_done(char *full_page) {
628document_headers_done (char *full_page) 654 const char *body;
629{
630 const char *body;
631 655
632 for (body = full_page; *body; body++) { 656 for (body = full_page; *body; body++) {
633 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3)) 657 if (!strncmp(body, "\n\n", 2) || !strncmp(body, "\n\r\n", 3)) {
634 break; 658 break;
635 } 659 }
660 }
636 661
637 if (!*body) 662 if (!*body) {
638 return 0; /* haven't read end of headers yet */ 663 return 0; /* haven't read end of headers yet */
664 }
639 665
640 full_page[body - full_page] = 0; 666 full_page[body - full_page] = 0;
641 return 1; 667 return 1;
642} 668}
643 669
644static time_t 670static time_t parse_time_string(const char *string) {
645parse_time_string (const char *string) 671 struct tm tm;
646{ 672 time_t t;
647 struct tm tm; 673 memset(&tm, 0, sizeof(tm));
648 time_t t; 674
649 memset (&tm, 0, sizeof(tm)); 675 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
650 676
651 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ 677 if (isupper(string[0]) && /* Tue */
652 678 islower(string[1]) && islower(string[2]) && ',' == string[3] && ' ' == string[4] &&
653 if (isupper (string[0]) && /* Tue */ 679 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
654 islower (string[1]) && 680 isdigit(string[6]) && ' ' == string[7] && isupper(string[8]) && /* Dec */
655 islower (string[2]) && 681 islower(string[9]) && islower(string[10]) && ' ' == string[11] &&
656 ',' == string[3] && 682 isdigit(string[12]) && /* 2001 */
657 ' ' == string[4] && 683 isdigit(string[13]) && isdigit(string[14]) && isdigit(string[15]) && ' ' == string[16] &&
658 (isdigit(string[5]) || string[5] == ' ') && /* 25 */ 684 isdigit(string[17]) && /* 02: */
659 isdigit (string[6]) && 685 isdigit(string[18]) && ':' == string[19] && isdigit(string[20]) && /* 59: */
660 ' ' == string[7] && 686 isdigit(string[21]) && ':' == string[22] && isdigit(string[23]) && /* 03 */
661 isupper (string[8]) && /* Dec */ 687 isdigit(string[24]) && ' ' == string[25] && 'G' == string[26] && /* GMT */
662 islower (string[9]) && 688 'M' == string[27] && /* GMT */
663 islower (string[10]) && 689 'T' == string[28]) {
664 ' ' == string[11] && 690
665 isdigit (string[12]) && /* 2001 */ 691 tm.tm_sec = 10 * (string[23] - '0') + (string[24] - '0');
666 isdigit (string[13]) && 692 tm.tm_min = 10 * (string[20] - '0') + (string[21] - '0');
667 isdigit (string[14]) && 693 tm.tm_hour = 10 * (string[17] - '0') + (string[18] - '0');
668 isdigit (string[15]) && 694 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5] - '0') + (string[6] - '0');
669 ' ' == string[16] && 695 tm.tm_mon = (!strncmp(string + 8, "Jan", 3) ? 0
670 isdigit (string[17]) && /* 02: */ 696 : !strncmp(string + 8, "Feb", 3) ? 1
671 isdigit (string[18]) && 697 : !strncmp(string + 8, "Mar", 3) ? 2
672 ':' == string[19] && 698 : !strncmp(string + 8, "Apr", 3) ? 3
673 isdigit (string[20]) && /* 59: */ 699 : !strncmp(string + 8, "May", 3) ? 4
674 isdigit (string[21]) && 700 : !strncmp(string + 8, "Jun", 3) ? 5
675 ':' == string[22] && 701 : !strncmp(string + 8, "Jul", 3) ? 6
676 isdigit (string[23]) && /* 03 */ 702 : !strncmp(string + 8, "Aug", 3) ? 7
677 isdigit (string[24]) && 703 : !strncmp(string + 8, "Sep", 3) ? 8
678 ' ' == string[25] && 704 : !strncmp(string + 8, "Oct", 3) ? 9
679 'G' == string[26] && /* GMT */ 705 : !strncmp(string + 8, "Nov", 3) ? 10
680 'M' == string[27] && /* GMT */ 706 : !strncmp(string + 8, "Dec", 3) ? 11
681 'T' == string[28]) { 707 : -1);
682 708 tm.tm_year = ((1000 * (string[12] - '0') + 100 * (string[13] - '0') +
683 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0'); 709 10 * (string[14] - '0') + (string[15] - '0')) -
684 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0'); 710 1900);
685 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0'); 711
686 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0'); 712 tm.tm_isdst = 0; /* GMT is never in DST, right? */
687 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 : 713
688 !strncmp (string+8, "Feb", 3) ? 1 : 714 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) {
689 !strncmp (string+8, "Mar", 3) ? 2 : 715 return 0;
690 !strncmp (string+8, "Apr", 3) ? 3 : 716 }
691 !strncmp (string+8, "May", 3) ? 4 : 717
692 !strncmp (string+8, "Jun", 3) ? 5 : 718 /*
693 !strncmp (string+8, "Jul", 3) ? 6 : 719 This is actually wrong: we need to subtract the local timezone
694 !strncmp (string+8, "Aug", 3) ? 7 : 720 offset from GMT from this value. But, that's ok in this usage,
695 !strncmp (string+8, "Sep", 3) ? 8 : 721 because we only comparing these two GMT dates against each other,
696 !strncmp (string+8, "Oct", 3) ? 9 : 722 so it doesn't matter what time zone we parse them in.
697 !strncmp (string+8, "Nov", 3) ? 10 : 723 */
698 !strncmp (string+8, "Dec", 3) ? 11 : 724
699 -1); 725 t = mktime(&tm);
700 tm.tm_year = ((1000 * (string[12]-'0') + 726 if (t == (time_t)-1) {
701 100 * (string[13]-'0') + 727 t = 0;
702 10 * (string[14]-'0') + 728 }
703 (string[15]-'0')) 729
704 - 1900); 730 if (verbose) {
705 731 const char *s = string;
706 tm.tm_isdst = 0; /* GMT is never in DST, right? */ 732 while (*s && *s != '\r' && *s != '\n') {
707 733 fputc(*s++, stdout);
708 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) 734 }
709 return 0; 735 printf(" ==> %lu\n", (unsigned long)t);
710 736 }
711 /* 737
712 This is actually wrong: we need to subtract the local timezone 738 return t;
713 offset from GMT from this value. But, that's ok in this usage, 739 }
714 because we only comparing these two GMT dates against each other, 740 return 0;
715 so it doesn't matter what time zone we parse them in.
716 */
717
718 t = mktime (&tm);
719 if (t == (time_t) -1) t = 0;
720
721 if (verbose) {
722 const char *s = string;
723 while (*s && *s != '\r' && *s != '\n')
724 fputc (*s++, stdout);
725 printf (" ==> %lu\n", (unsigned long) t);
726 }
727
728 return t;
729
730 } else {
731 return 0;
732 }
733} 741}
734 742
735/* Checks if the server 'reply' is one of the expected 'statuscodes' */ 743/* Checks if the server 'reply' is one of the expected 'statuscodes' */
736static int 744static int expected_statuscode(const char *reply, const char *statuscodes) {
737expected_statuscode (const char *reply, const char *statuscodes) 745 char *expected;
738{ 746 char *code;
739 char *expected, *code; 747 int result = 0;
740 int result = 0; 748
741 749 if ((expected = strdup(statuscodes)) == NULL) {
742 if ((expected = strdup (statuscodes)) == NULL) 750 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
743 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 751 }
744 752
745 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) 753 for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) {
746 if (strstr (reply, code) != NULL) { 754 if (strstr(reply, code) != NULL) {
747 result = 1; 755 result = 1;
748 break; 756 break;
749 } 757 }
750 758 }
751 free (expected); 759
752 return result; 760 free(expected);
761 return result;
753} 762}
754 763
755static int 764static int check_document_dates(const char *headers, char **msg) {
756check_document_dates (const char *headers, char **msg) 765 const char *s;
757{ 766 char *server_date = 0;
758 const char *s; 767 char *document_date = 0;
759 char *server_date = 0; 768 int date_result = STATE_OK;
760 char *document_date = 0; 769
761 int date_result = STATE_OK; 770 s = headers;
762 771 while (*s) {
763 s = headers; 772 const char *field = s;
764 while (*s) { 773 const char *value = 0;
765 const char *field = s; 774
766 const char *value = 0; 775 /* Find the end of the header field */
767 776 while (*s && !isspace(*s) && *s != ':') {
768 /* Find the end of the header field */ 777 s++;
769 while (*s && !isspace(*s) && *s != ':') 778 }
770 s++; 779
771 780 /* Remember the header value, if any. */
772 /* Remember the header value, if any. */ 781 if (*s == ':') {
773 if (*s == ':') 782 value = ++s;
774 value = ++s; 783 }
775 784
776 /* Skip to the end of the header, including continuation lines. */ 785 /* Skip to the end of the header, including continuation lines. */
777 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) 786 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) {
778 s++; 787 s++;
779 788 }
780 /* Avoid stepping over end-of-string marker */ 789
781 if (*s) 790 /* Avoid stepping over end-of-string marker */
782 s++; 791 if (*s) {
783 792 s++;
784 /* Process this header. */ 793 }
785 if (value && value > field+2) { 794
786 char *ff = (char *) malloc (value-field); 795 /* Process this header. */
787 char *ss = ff; 796 if (value && value > field + 2) {
788 while (field < value-1) 797 char *ff = (char *)malloc(value - field);
789 *ss++ = tolower(*field++); 798 char *ss = ff;
790 *ss++ = 0; 799 while (field < value - 1) {
791 800 *ss++ = tolower(*field++);
792 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) { 801 }
793 const char *e; 802 *ss++ = 0;
794 while (*value && isspace (*value)) 803
795 value++; 804 if (!strcmp(ff, "date") || !strcmp(ff, "last-modified")) {
796 for (e = value; *e && *e != '\r' && *e != '\n'; e++) 805 const char *e;
797 ; 806 while (*value && isspace(*value)) {
798 ss = (char *) malloc (e - value + 1); 807 value++;
799 strncpy (ss, value, e - value); 808 }
800 ss[e - value] = 0; 809 for (e = value; *e && *e != '\r' && *e != '\n'; e++) {
801 if (!strcmp (ff, "date")) { 810 ;
802 if (server_date) free (server_date); 811 }
803 server_date = ss; 812 ss = (char *)malloc(e - value + 1);
804 } else { 813 strncpy(ss, value, e - value);
805 if (document_date) free (document_date); 814 ss[e - value] = 0;
806 document_date = ss; 815 if (!strcmp(ff, "date")) {
807 } 816 if (server_date) {
808 } 817 free(server_date);
809 free (ff); 818 }
810 } 819 server_date = ss;
811 } 820 } else {
812 821 if (document_date) {
813 /* Done parsing the body. Now check the dates we (hopefully) parsed. */ 822 free(document_date);
814 if (!server_date || !*server_date) { 823 }
815 xasprintf (msg, _("%sServer date unknown, "), *msg); 824 document_date = ss;
816 date_result = max_state_alt(STATE_UNKNOWN, date_result); 825 }
817 } else if (!document_date || !*document_date) { 826 }
818 xasprintf (msg, _("%sDocument modification date unknown, "), *msg); 827 free(ff);
819 date_result = max_state_alt(STATE_CRITICAL, date_result); 828 }
820 } else { 829 }
821 time_t srv_data = parse_time_string (server_date); 830
822 time_t doc_data = parse_time_string (document_date); 831 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
823 832 if (!server_date || !*server_date) {
824 if (srv_data <= 0) { 833 xasprintf(msg, _("%sServer date unknown, "), *msg);
825 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); 834 date_result = max_state_alt(STATE_UNKNOWN, date_result);
826 date_result = max_state_alt(STATE_CRITICAL, date_result); 835 } else if (!document_date || !*document_date) {
827 } else if (doc_data <= 0) { 836 xasprintf(msg, _("%sDocument modification date unknown, "), *msg);
828 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); 837 date_result = max_state_alt(STATE_CRITICAL, date_result);
829 date_result = max_state_alt(STATE_CRITICAL, date_result); 838 } else {
830 } else if (doc_data > srv_data + 30) { 839 time_t srv_data = parse_time_string(server_date);
831 xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); 840 time_t doc_data = parse_time_string(document_date);
832 date_result = max_state_alt(STATE_CRITICAL, date_result); 841
833 } else if (doc_data < srv_data - maximum_age) { 842 if (srv_data <= 0) {
834 int n = (srv_data - doc_data); 843 xasprintf(msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
835 if (n > (60 * 60 * 24 * 2)) { 844 date_result = max_state_alt(STATE_CRITICAL, date_result);
836 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); 845 } else if (doc_data <= 0) {
837 date_result = max_state_alt(STATE_CRITICAL, date_result); 846 xasprintf(msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
838 } else { 847 date_result = max_state_alt(STATE_CRITICAL, date_result);
839 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); 848 } else if (doc_data > srv_data + 30) {
840 date_result = max_state_alt(STATE_CRITICAL, date_result); 849 xasprintf(msg, _("%sDocument is %d seconds in the future, "), *msg,
841 } 850 (int)doc_data - (int)srv_data);
842 } 851 date_result = max_state_alt(STATE_CRITICAL, date_result);
843 free (server_date); 852 } else if (doc_data < srv_data - maximum_age) {
844 free (document_date); 853 int n = (srv_data - doc_data);
845 } 854 if (n > (60 * 60 * 24 * 2)) {
846 return date_result; 855 xasprintf(msg, _("%sLast modified %.1f days ago, "), *msg,
856 ((float)n) / (60 * 60 * 24));
857 date_result = max_state_alt(STATE_CRITICAL, date_result);
858 } else {
859 xasprintf(msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60),
860 (n / 60) % 60, n % 60);
861 date_result = max_state_alt(STATE_CRITICAL, date_result);
862 }
863 }
864 free(server_date);
865 free(document_date);
866 }
867 return date_result;
847} 868}
848 869
849int 870int get_content_length(const char *headers) {
850get_content_length (const char *headers) 871 const char *s;
851{ 872 int content_length = 0;
852 const char *s; 873
853 int content_length = 0; 874 s = headers;
854 875 while (*s) {
855 s = headers; 876 const char *field = s;
856 while (*s) { 877 const char *value = 0;
857 const char *field = s; 878
858 const char *value = 0; 879 /* Find the end of the header field */
859 880 while (*s && !isspace(*s) && *s != ':') {
860 /* Find the end of the header field */ 881 s++;
861 while (*s && !isspace(*s) && *s != ':') 882 }
862 s++; 883
863 884 /* Remember the header value, if any. */
864 /* Remember the header value, if any. */ 885 if (*s == ':') {
865 if (*s == ':') 886 value = ++s;
866 value = ++s; 887 }
867 888
868 /* Skip to the end of the header, including continuation lines. */ 889 /* Skip to the end of the header, including continuation lines. */
869 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) 890 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) {
870 s++; 891 s++;
871 892 }
872 /* Avoid stepping over end-of-string marker */ 893
873 if (*s) 894 /* Avoid stepping over end-of-string marker */
874 s++; 895 if (*s) {
875 896 s++;
876 /* Process this header. */ 897 }
877 if (value && value > field+2) { 898
878 char *ff = (char *) malloc (value-field); 899 /* Process this header. */
879 char *ss = ff; 900 if (value && value > field + 2) {
880 while (field < value-1) 901 char *ff = (char *)malloc(value - field);
881 *ss++ = tolower(*field++); 902 char *ss = ff;
882 *ss++ = 0; 903 while (field < value - 1) {
883 904 *ss++ = tolower(*field++);
884 if (!strcmp (ff, "content-length")) { 905 }
885 const char *e; 906 *ss++ = 0;
886 while (*value && isspace (*value)) 907
887 value++; 908 if (!strcmp(ff, "content-length")) {
888 for (e = value; *e && *e != '\r' && *e != '\n'; e++) 909 const char *e;
889 ; 910 while (*value && isspace(*value)) {
890 ss = (char *) malloc (e - value + 1); 911 value++;
891 strncpy (ss, value, e - value); 912 }
892 ss[e - value] = 0; 913 for (e = value; *e && *e != '\r' && *e != '\n'; e++) {
893 content_length = atoi(ss); 914 ;
894 free (ss); 915 }
895 } 916 ss = (char *)malloc(e - value + 1);
896 free (ff); 917 strncpy(ss, value, e - value);
897 } 918 ss[e - value] = 0;
898 } 919 content_length = atoi(ss);
899 return (content_length); 920 free(ss);
921 }
922 free(ff);
923 }
924 }
925 return (content_length);
900} 926}
901 927
902char * 928char *prepend_slash(char *path) {
903prepend_slash (char *path) 929 char *newpath;
904{
905 char *newpath;
906 930
907 if (path[0] == '/') 931 if (path[0] == '/') {
908 return path; 932 return path;
933 }
909 934
910 if ((newpath = malloc (strlen(path) + 2)) == NULL) 935 if ((newpath = malloc(strlen(path) + 2)) == NULL) {
911 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); 936 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
912 newpath[0] = '/'; 937 }
913 strcpy (newpath + 1, path); 938 newpath[0] = '/';
914 free (path); 939 strcpy(newpath + 1, path);
915 return newpath; 940 free(path);
941 return newpath;
916} 942}
917 943
918int 944int check_http(void) {
919check_http (void) 945 char *msg;
920{ 946 char *status_line;
921 char *msg; 947 char *status_code;
922 char *status_line; 948 char *header;
923 char *status_code; 949 char *page;
924 char *header; 950 char *auth;
925 char *page; 951 int http_status;
926 char *auth; 952 int i = 0;
927 int http_status; 953 size_t pagesize = 0;
928 int i = 0; 954 char *full_page;
929 size_t pagesize = 0; 955 char *full_page_new;
930 char *full_page; 956 char *buf;
931 char *full_page_new; 957 char *pos;
932 char *buf; 958 long microsec = 0L;
933 char *pos; 959 double elapsed_time = 0.0;
934 long microsec = 0L; 960 long microsec_connect = 0L;
935 double elapsed_time = 0.0; 961 double elapsed_time_connect = 0.0;
936 long microsec_connect = 0L; 962 long microsec_ssl = 0L;
937 double elapsed_time_connect = 0.0; 963 double elapsed_time_ssl = 0.0;
938 long microsec_ssl = 0L; 964 long microsec_firstbyte = 0L;
939 double elapsed_time_ssl = 0.0; 965 double elapsed_time_firstbyte = 0.0;
940 long microsec_firstbyte = 0L; 966 long microsec_headers = 0L;
941 double elapsed_time_firstbyte = 0.0; 967 double elapsed_time_headers = 0.0;
942 long microsec_headers = 0L; 968 long microsec_transfer = 0L;
943 double elapsed_time_headers = 0.0; 969 double elapsed_time_transfer = 0.0;
944 long microsec_transfer = 0L; 970 int page_len = 0;
945 double elapsed_time_transfer = 0.0; 971 int result = STATE_OK;
946 int page_len = 0; 972 char *force_host_header = NULL;
947 int result = STATE_OK; 973
948 char *force_host_header = NULL; 974 /* try to connect to the host at the given port number */
949 975 gettimeofday(&tv_temp, NULL);
950 /* try to connect to the host at the given port number */ 976 if (my_tcp_connect(server_address, server_port, &sd) != STATE_OK) {
951 gettimeofday (&tv_temp, NULL); 977 die(STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
952 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) 978 }
953 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n")); 979 microsec_connect = deltime(tv_temp);
954 microsec_connect = deltime (tv_temp); 980
955 981 /* if we are called with the -I option, the -j method is CONNECT and */
956 /* if we are called with the -I option, the -j method is CONNECT and */ 982 /* we received -S for SSL, then we tunnel the request through a proxy*/
957 /* we received -S for SSL, then we tunnel the request through a proxy*/ 983 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
958 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */ 984
959 985 if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL &&
960 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 986 use_ssl) {
961 && host_name != NULL && use_ssl == true) { 987
962 988 if (verbose) {
963 if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT); 989 printf("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address,
964 asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent); 990 server_port, host_name, HTTPS_PORT);
965 if (strlen(proxy_auth)) { 991 }
966 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); 992 asprintf(&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT,
967 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); 993 user_agent);
968 } 994 if (strlen(proxy_auth)) {
969 /* optionally send any other header tag */ 995 base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth);
970 if (http_opt_headers_count) { 996 xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
971 for (i = 0; i < http_opt_headers_count ; i++) { 997 }
972 if (force_host_header != http_opt_headers[i]) { 998 /* optionally send any other header tag */
973 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); 999 if (http_opt_headers_count) {
974 } 1000 for (i = 0; i < http_opt_headers_count; i++) {
975 } 1001 if (force_host_header != http_opt_headers[i]) {
976 /* This cannot be free'd here because a redirection will then try to access this and segfault */ 1002 xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]);
977 /* Covered in a testcase in tests/check_http.t */ 1003 }
978 /* free(http_opt_headers); */ 1004 }
979 } 1005 /* This cannot be free'd here because a redirection will then try to access this and
980 asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf); 1006 * segfault */
981 asprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1007 /* Covered in a testcase in tests/check_http.t */
982 /* we finished our request, send empty line with CRLF */ 1008 /* free(http_opt_headers); */
983 asprintf (&buf, "%s%s", buf, CRLF); 1009 }
984 if (verbose) printf ("%s\n", buf); 1010 asprintf(&buf, "%sProxy-Connection: keep-alive\r\n", buf);
985 send(sd, buf, strlen (buf), 0); 1011 asprintf(&buf, "%sHost: %s\r\n", buf, host_name);
986 buf[0]='\0'; 1012 /* we finished our request, send empty line with CRLF */
987 1013 asprintf(&buf, "%s%s", buf, CRLF);
988 if (verbose) printf ("Receive response from proxy\n"); 1014 if (verbose) {
989 read (sd, buffer, MAX_INPUT_BUFFER-1); 1015 printf("%s\n", buf);
990 if (verbose) printf ("%s", buffer); 1016 }
991 /* Here we should check if we got HTTP/1.1 200 Connection established */ 1017 send(sd, buf, strlen(buf), 0);
992 } 1018 buf[0] = '\0';
1019
1020 if (verbose) {
1021 printf("Receive response from proxy\n");
1022 }
1023 read(sd, buffer, MAX_INPUT_BUFFER - 1);
1024 if (verbose) {
1025 printf("%s", buffer);
1026 }
1027 /* Here we should check if we got HTTP/1.1 200 Connection established */
1028 }
993#ifdef HAVE_SSL 1029#ifdef HAVE_SSL
994 elapsed_time_connect = (double)microsec_connect / 1.0e6; 1030 elapsed_time_connect = (double)microsec_connect / 1.0e6;
995 if (use_ssl == true) { 1031 if (use_ssl) {
996 gettimeofday (&tv_temp, NULL); 1032 gettimeofday(&tv_temp, NULL);
997 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey); 1033 result = np_net_ssl_init_with_hostname_version_and_cert(
998 if (verbose) printf ("SSL initialized\n"); 1034 sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
999 if (result != STATE_OK) 1035 if (verbose) {
1000 die (STATE_CRITICAL, NULL); 1036 printf("SSL initialized\n");
1001 microsec_ssl = deltime (tv_temp); 1037 }
1002 elapsed_time_ssl = (double)microsec_ssl / 1.0e6; 1038 if (result != STATE_OK) {
1003 if (check_cert == true) { 1039 die(STATE_CRITICAL, NULL);
1004 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 1040 }
1005 if (continue_after_check_cert == false) { 1041 microsec_ssl = deltime(tv_temp);
1006 if (sd) close(sd); 1042 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
1007 np_net_ssl_cleanup(); 1043 if (check_cert) {
1008 return result; 1044 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
1009 } 1045 if (!continue_after_check_cert) {
1010 } 1046 if (sd) {
1011 } 1047 close(sd);
1048 }
1049 np_net_ssl_cleanup();
1050 return result;
1051 }
1052 }
1053 }
1012#endif /* HAVE_SSL */ 1054#endif /* HAVE_SSL */
1013 1055
1014 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 1056 if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL &&
1015 && host_name != NULL && use_ssl == true) 1057 use_ssl) {
1016 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); 1058 asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url,
1017 else 1059 host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1018 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); 1060 } else {
1019 1061 asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method, server_url,
1020 /* tell HTTP/1.1 servers not to keep the connection alive */ 1062 host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1021 xasprintf (&buf, "%sConnection: close\r\n", buf); 1063 }
1022 1064
1023 /* check if Host header is explicitly set in options */ 1065 /* tell HTTP/1.1 servers not to keep the connection alive */
1024 if (http_opt_headers_count) { 1066 xasprintf(&buf, "%sConnection: close\r\n", buf);
1025 for (i = 0; i < http_opt_headers_count ; i++) { 1067
1026 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { 1068 /* check if Host header is explicitly set in options */
1027 force_host_header = http_opt_headers[i]; 1069 if (http_opt_headers_count) {
1028 } 1070 for (i = 0; i < http_opt_headers_count; i++) {
1029 } 1071 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
1030 } 1072 force_host_header = http_opt_headers[i];
1031 1073 }
1032 /* optionally send the host header info */ 1074 }
1033 if (host_name) { 1075 }
1034 if (force_host_header) { 1076
1035 xasprintf (&buf, "%s%s\r\n", buf, force_host_header); 1077 /* optionally send the host header info */
1036 } 1078 if (host_name) {
1037 else { 1079 if (force_host_header) {
1038 /* 1080 xasprintf(&buf, "%s%s\r\n", buf, force_host_header);
1039 * Specify the port only if we're using a non-default port (see RFC 2616, 1081 } else {
1040 * 14.23). Some server applications/configurations cause trouble if the 1082 /*
1041 * (default) port is explicitly specified in the "Host:" header line. 1083 * Specify the port only if we're using a non-default port (see RFC 2616,
1042 */ 1084 * 14.23). Some server applications/configurations cause trouble if the
1043 if ((use_ssl == false && virtual_port == HTTP_PORT) || 1085 * (default) port is explicitly specified in the "Host:" header line.
1044 (use_ssl == true && virtual_port == HTTPS_PORT) || 1086 */
1045 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 1087 if ((!use_ssl && virtual_port == HTTP_PORT) ||
1046 && host_name != NULL && use_ssl == true)) 1088 (use_ssl && virtual_port == HTTPS_PORT) ||
1047 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1089 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 &&
1048 else 1090 host_name != NULL && use_ssl)) {
1049 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port); 1091 xasprintf(&buf, "%sHost: %s\r\n", buf, host_name);
1050 } 1092 } else {
1051 } 1093 xasprintf(&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port);
1052 1094 }
1053 /* optionally send any other header tag */ 1095 }
1054 if (http_opt_headers_count) { 1096 }
1055 for (i = 0; i < http_opt_headers_count ; i++) { 1097
1056 if (force_host_header != http_opt_headers[i]) { 1098 /* optionally send any other header tag */
1057 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); 1099 if (http_opt_headers_count) {
1058 } 1100 for (i = 0; i < http_opt_headers_count; i++) {
1059 } 1101 if (force_host_header != http_opt_headers[i]) {
1060 /* This cannot be free'd here because a redirection will then try to access this and segfault */ 1102 xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]);
1061 /* Covered in a testcase in tests/check_http.t */ 1103 }
1062 /* free(http_opt_headers); */ 1104 }
1063 } 1105 /* This cannot be free'd here because a redirection will then try to access this and
1064 1106 * segfault */
1065 /* optionally send the authentication info */ 1107 /* Covered in a testcase in tests/check_http.t */
1066 if (strlen(user_auth)) { 1108 /* free(http_opt_headers); */
1067 base64_encode_alloc (user_auth, strlen (user_auth), &auth); 1109 }
1068 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth); 1110
1069 } 1111 /* optionally send the authentication info */
1070 1112 if (strlen(user_auth)) {
1071 /* optionally send the proxy authentication info */ 1113 base64_encode_alloc(user_auth, strlen(user_auth), &auth);
1072 if (strlen(proxy_auth)) { 1114 xasprintf(&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
1073 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); 1115 }
1074 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); 1116
1075 } 1117 /* optionally send the proxy authentication info */
1076 1118 if (strlen(proxy_auth)) {
1077 /* either send http POST data (any data, not only POST)*/ 1119 base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth);
1078 if (http_post_data) { 1120 xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
1079 if (http_content_type) { 1121 }
1080 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type); 1122
1081 } else { 1123 /* either send http POST data (any data, not only POST)*/
1082 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf); 1124 if (http_post_data) {
1083 } 1125 if (http_content_type) {
1084 1126 xasprintf(&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
1085 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data)); 1127 } else {
1086 xasprintf (&buf, "%s%s", buf, http_post_data); 1128 xasprintf(&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
1087 } else { 1129 }
1088 /* or just a newline so the server knows we're done with the request */ 1130
1089 xasprintf (&buf, "%s%s", buf, CRLF); 1131 xasprintf(&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen(http_post_data));
1090 } 1132 xasprintf(&buf, "%s%s", buf, http_post_data);
1091 1133 } else {
1092 if (verbose) printf ("%s\n", buf); 1134 /* or just a newline so the server knows we're done with the request */
1093 gettimeofday (&tv_temp, NULL); 1135 xasprintf(&buf, "%s%s", buf, CRLF);
1094 my_send (buf, strlen (buf)); 1136 }
1095 microsec_headers = deltime (tv_temp); 1137
1096 elapsed_time_headers = (double)microsec_headers / 1.0e6; 1138 if (verbose) {
1097 1139 printf("%s\n", buf);
1098 /* fetch the page */ 1140 }
1099 full_page = strdup(""); 1141 gettimeofday(&tv_temp, NULL);
1100 gettimeofday (&tv_temp, NULL); 1142 my_send(buf, strlen(buf));
1101 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) { 1143 microsec_headers = deltime(tv_temp);
1102 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) { 1144 elapsed_time_headers = (double)microsec_headers / 1.0e6;
1103 microsec_firstbyte = deltime (tv_temp); 1145
1104 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6; 1146 /* fetch the page */
1105 } 1147 full_page = strdup("");
1106 while ((pos = memchr(buffer, '\0', i))) { 1148 gettimeofday(&tv_temp, NULL);
1107 /* replace nul character with a blank */ 1149 while ((i = my_recv(buffer, MAX_INPUT_BUFFER - 1)) > 0) {
1108 *pos = ' '; 1150 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
1109 } 1151 microsec_firstbyte = deltime(tv_temp);
1110 buffer[i] = '\0'; 1152 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
1111 1153 }
1112 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) 1154 while ((pos = memchr(buffer, '\0', i))) {
1113 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n")); 1155 /* replace nul character with a blank */
1114 1156 *pos = ' ';
1115 memmove(&full_page_new[pagesize], buffer, i + 1); 1157 }
1116 1158 buffer[i] = '\0';
1117 full_page = full_page_new; 1159
1118 1160 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) {
1119 pagesize += i; 1161 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1120 1162 }
1121 if (no_body && document_headers_done (full_page)) { 1163
1122 i = 0; 1164 memmove(&full_page_new[pagesize], buffer, i + 1);
1123 break; 1165
1124 } 1166 full_page = full_page_new;
1125 } 1167
1126 microsec_transfer = deltime (tv_temp); 1168 pagesize += i;
1127 elapsed_time_transfer = (double)microsec_transfer / 1.0e6; 1169
1128 1170 if (no_body && document_headers_done(full_page)) {
1129 if (i < 0 && errno != ECONNRESET) { 1171 i = 0;
1130 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n")); 1172 break;
1131 } 1173 }
1132 1174 }
1133 /* return a CRITICAL status if we couldn't read any data */ 1175 microsec_transfer = deltime(tv_temp);
1134 if (pagesize == (size_t) 0) 1176 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1135 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n")); 1177
1136 1178 if (i < 0 && errno != ECONNRESET) {
1137 /* close the connection */ 1179 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1138 if (sd) close(sd); 1180 }
1181
1182 /* return a CRITICAL status if we couldn't read any data */
1183 if (pagesize == (size_t)0) {
1184 die(STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1185 }
1186
1187 /* close the connection */
1188 if (sd) {
1189 close(sd);
1190 }
1139#ifdef HAVE_SSL 1191#ifdef HAVE_SSL
1140 np_net_ssl_cleanup(); 1192 np_net_ssl_cleanup();
1141#endif 1193#endif
1142 1194
1143 /* Save check time */ 1195 /* Save check time */
1144 microsec = deltime (tv); 1196 microsec = deltime(tv);
1145 elapsed_time = (double)microsec / 1.0e6; 1197 elapsed_time = (double)microsec / 1.0e6;
1146 1198
1147 /* leave full_page untouched so we can free it later */ 1199 /* leave full_page untouched so we can free it later */
1148 page = full_page; 1200 page = full_page;
1149 1201
1150 if (verbose) 1202 if (verbose) {
1151 printf ("%s://%s:%d%s is %d characters\n", 1203 printf("%s://%s:%d%s is %d characters\n", use_ssl ? "https" : "http", server_address,
1152 use_ssl ? "https" : "http", server_address, 1204 server_port, server_url, (int)pagesize);
1153 server_port, server_url, (int)pagesize); 1205 }
1154 1206
1155 /* find status line and null-terminate it */ 1207 /* find status line and null-terminate it */
1156 status_line = page; 1208 status_line = page;
1157 page += (size_t) strcspn (page, "\r\n"); 1209 page += (size_t)strcspn(page, "\r\n");
1158 pos = page; 1210 pos = page;
1159 page += (size_t) strspn (page, "\r\n"); 1211 page += (size_t)strspn(page, "\r\n");
1160 status_line[strcspn(status_line, "\r\n")] = 0; 1212 status_line[strcspn(status_line, "\r\n")] = 0;
1161 strip (status_line); 1213 strip(status_line);
1162 if (verbose) 1214 if (verbose) {
1163 printf ("STATUS: %s\n", status_line); 1215 printf("STATUS: %s\n", status_line);
1164 1216 }
1165 /* find header info and null-terminate it */ 1217
1166 header = page; 1218 /* find header info and null-terminate it */
1167 while (strcspn (page, "\r\n") > 0) { 1219 header = page;
1168 page += (size_t) strcspn (page, "\r\n"); 1220 while (strcspn(page, "\r\n") > 0) {
1169 pos = page; 1221 page += (size_t)strcspn(page, "\r\n");
1170 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) || 1222 pos = page;
1171 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2)) 1223 if ((strspn(page, "\r") == 1 && strspn(page, "\r\n") >= 2) ||
1172 page += (size_t) 2; 1224 (strspn(page, "\n") == 1 && strspn(page, "\r\n") >= 2)) {
1173 else 1225 page += (size_t)2;
1174 page += (size_t) 1; 1226 } else {
1175 } 1227 page += (size_t)1;
1176 page += (size_t) strspn (page, "\r\n"); 1228 }
1177 header[pos - header] = 0; 1229 }
1178 if (verbose) 1230 page += (size_t)strspn(page, "\r\n");
1179 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, 1231 header[pos - header] = 0;
1180 (no_body ? " [[ skipped ]]" : page)); 1232 if (verbose) {
1181 1233 printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1182 /* make sure the status line matches the response we are looking for */ 1234 (no_body ? " [[ skipped ]]" : page));
1183 if (!expected_statuscode (status_line, server_expect)) { 1235 }
1184 if (server_port == HTTP_PORT) 1236
1185 xasprintf (&msg, 1237 /* make sure the status line matches the response we are looking for */
1186 _("Invalid HTTP response received from host: %s\n"), 1238 if (!expected_statuscode(status_line, server_expect)) {
1187 status_line); 1239 if (server_port == HTTP_PORT) {
1188 else 1240 xasprintf(&msg, _("Invalid HTTP response received from host: %s\n"), status_line);
1189 xasprintf (&msg, 1241 } else {
1190 _("Invalid HTTP response received from host on port %d: %s\n"), 1242 xasprintf(&msg, _("Invalid HTTP response received from host on port %d: %s\n"),
1191 server_port, status_line); 1243 server_port, status_line);
1192 if (show_body) 1244 }
1193 xasprintf (&msg, _("%s\n%s"), msg, page); 1245 if (show_body) {
1194 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg); 1246 xasprintf(&msg, _("%s\n%s"), msg, page);
1195 } 1247 }
1196 1248 die(STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
1197 /* Bypass normal status line check if server_expect was set by user and not default */ 1249 }
1198 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */ 1250
1199 if ( server_expect_yn ) { 1251 /* Bypass normal status line check if server_expect was set by user and not default */
1200 xasprintf (&msg, 1252 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1201 _("Status line output matched \"%s\" - "), server_expect); 1253 if (server_expect_yn) {
1202 if (verbose) 1254 xasprintf(&msg, _("Status line output matched \"%s\" - "), server_expect);
1203 printf ("%s\n",msg); 1255 if (verbose) {
1204 } 1256 printf("%s\n", msg);
1205 else { 1257 }
1206 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ 1258 } else {
1207 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ 1259 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1208 /* Status-Code = 3 DIGITS */ 1260 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1209 1261 /* Status-Code = 3 DIGITS */
1210 status_code = strchr (status_line, ' ') + sizeof (char); 1262
1211 if (strspn (status_code, "1234567890") != 3) 1263 status_code = strchr(status_line, ' ') + sizeof(char);
1212 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line); 1264 if (strspn(status_code, "1234567890") != 3) {
1213 1265 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
1214 http_status = atoi (status_code); 1266 }
1215 1267
1216 /* check the return code */ 1268 http_status = atoi(status_code);
1217 1269
1218 if (http_status >= 600 || http_status < 100) { 1270 /* check the return code */
1219 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line); 1271
1220 } 1272 if (http_status >= 600 || http_status < 100) {
1221 /* server errors result in a critical state */ 1273 die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1222 else if (http_status >= 500) { 1274 }
1223 xasprintf (&msg, _("%s - "), status_line); 1275 /* server errors result in a critical state */
1224 result = STATE_CRITICAL; 1276 else if (http_status >= 500) {
1225 } 1277 xasprintf(&msg, _("%s - "), status_line);
1226 /* client errors result in a warning state */ 1278 result = STATE_CRITICAL;
1227 else if (http_status >= 400) { 1279 }
1228 xasprintf (&msg, _("%s - "), status_line); 1280 /* client errors result in a warning state */
1229 result = max_state_alt(STATE_WARNING, result); 1281 else if (http_status >= 400) {
1230 } 1282 xasprintf(&msg, _("%s - "), status_line);
1231 /* check redirected page if specified */ 1283 result = max_state_alt(STATE_WARNING, result);
1232 else if (http_status >= 300) { 1284 }
1233 1285 /* check redirected page if specified */
1234 if (onredirect == STATE_DEPENDENT) 1286 else if (http_status >= 300) {
1235 redir (header, status_line); 1287
1236 else 1288 if (onredirect == STATE_DEPENDENT) {
1237 result = max_state_alt(onredirect, result); 1289 redir(header, status_line);
1238 xasprintf (&msg, _("%s - "), status_line); 1290 } else {
1239 } /* end if (http_status >= 300) */ 1291 result = max_state_alt(onredirect, result);
1240 else { 1292 }
1241 /* Print OK status anyway */ 1293 xasprintf(&msg, _("%s - "), status_line);
1242 xasprintf (&msg, _("%s - "), status_line); 1294 } /* end if (http_status >= 300) */
1243 } 1295 else {
1244 1296 /* Print OK status anyway */
1245 } /* end else (server_expect_yn) */ 1297 xasprintf(&msg, _("%s - "), status_line);
1246 1298 }
1247 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */ 1299
1248 alarm (0); 1300 } /* end else (server_expect_yn) */
1249 1301
1250 if (maximum_age >= 0) { 1302 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1251 result = max_state_alt(check_document_dates(header, &msg), result); 1303 alarm(0);
1252 } 1304
1253 1305 if (maximum_age >= 0) {
1254 /* Page and Header content checks go here */ 1306 result = max_state_alt(check_document_dates(header, &msg), result);
1255 if (strlen(header_expect) > 0) { 1307 }
1256 if (strstr(header, header_expect) == NULL) { 1308
1257 // We did not find the header, the rest is for building the output and setting the state 1309 /* Page and Header content checks go here */
1258 char output_header_search[30] = ""; 1310 if (strlen(header_expect) > 0) {
1259 1311 if (strstr(header, header_expect) == NULL) {
1260 strncpy(&output_header_search[0], header_expect, 1312 // We did not find the header, the rest is for building the output and setting the state
1261 sizeof(output_header_search)); 1313 char output_header_search[30] = "";
1262 1314
1263 if (output_header_search[sizeof(output_header_search) - 1] != '\0') { 1315 strncpy(&output_header_search[0], header_expect, sizeof(output_header_search));
1264 bcopy("...", 1316
1265 &output_header_search[sizeof(output_header_search) - 4], 1317 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1266 4); 1318 bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4);
1267 } 1319 }
1268 1320
1269 xasprintf (&msg, 1321 xasprintf(&msg, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg,
1270 _("%sheader '%s' not found on '%s://%s:%d%s', "), 1322 output_header_search, use_ssl ? "https" : "http",
1271 msg, 1323 host_name ? host_name : server_address, server_port, server_url);
1272 output_header_search, use_ssl ? "https" : "http", 1324
1273 host_name ? host_name : server_address, server_port, 1325 result = STATE_CRITICAL;
1274 server_url); 1326 }
1275 1327 }
1276 result = STATE_CRITICAL; 1328
1277 } 1329 // At this point we should test if the content is chunked and unchunk it, so
1278 } 1330 // it can be searched (and possibly printed)
1279 1331 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *";
1280 // At this point we should test if the content is chunked and unchunk it, so 1332 regex_t chunked_header_regex;
1281 // it can be searched (and possibly printed) 1333
1282 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *"; 1334 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) {
1283 regex_t chunked_header_regex; 1335 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN),
1284 1336 "Failed to compile chunked_header_regex regex");
1285 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) { 1337 }
1286 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex"); 1338
1287 } 1339 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF
1288 1340 // it was found
1289 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF it was found 1341
1290 1342 if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) {
1291 if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) { 1343 if (verbose) {
1292 if (verbose) { 1344 printf("Found chunked content\n");
1293 printf("Found chunked content\n"); 1345 }
1294 } 1346 // We actually found the chunked header
1295 // We actually found the chunked header 1347 char *tmp = unchunk_content(page);
1296 char *tmp = unchunk_content(page); 1348 if (tmp == NULL) {
1297 if (tmp == NULL) { 1349 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN),
1298 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to unchunk message body"); 1350 "Failed to unchunk message body");
1299 } 1351 }
1300 page = tmp; 1352 page = tmp;
1301 } 1353 }
1302 1354
1303 if (strlen(string_expect) > 0) { 1355 if (strlen(string_expect) > 0) {
1304 if (!strstr(page, string_expect)) { 1356 if (!strstr(page, string_expect)) {
1305 // We found the string the body, the rest is for building the output 1357 // We found the string the body, the rest is for building the output
1306 char output_string_search[30] = ""; 1358 char output_string_search[30] = "";
1307 strncpy(&output_string_search[0], string_expect, 1359 strncpy(&output_string_search[0], string_expect, sizeof(output_string_search));
1308 sizeof(output_string_search)); 1360 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1309 if (output_string_search[sizeof(output_string_search) - 1] != '\0') { 1361 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4);
1310 bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 1362 }
1311 4); 1363 xasprintf(&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg,
1312 } 1364 output_string_search, use_ssl ? "https" : "http",
1313 xasprintf (&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); 1365 host_name ? host_name : server_address, server_port, server_url);
1314 result = STATE_CRITICAL; 1366 result = STATE_CRITICAL;
1315 } 1367 }
1316 } 1368 }
1317 1369
1318 if (strlen(regexp) > 0) { 1370 if (strlen(regexp) > 0) {
1319 errcode = regexec(&preg, page, REGS, pmatch, 0); 1371 errcode = regexec(&preg, page, REGS, pmatch, 0);
1320 if ((errcode == 0 && invert_regex == 0) || 1372 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1321 (errcode == REG_NOMATCH && invert_regex == 1)) { 1373 /* OK - No-op to avoid changing the logic around it */
1322 /* OK - No-op to avoid changing the logic around it */ 1374 result = max_state_alt(STATE_OK, result);
1323 result = max_state_alt(STATE_OK, result); 1375 } else if ((errcode == REG_NOMATCH && invert_regex == 0) ||
1324 } 1376 (errcode == 0 && invert_regex == 1)) {
1325 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) { 1377 if (invert_regex == 0) {
1326 if (invert_regex == 0) 1378 xasprintf(&msg, _("%spattern not found, "), msg);
1327 xasprintf (&msg, _("%spattern not found, "), msg); 1379 } else {
1328 else 1380 xasprintf(&msg, _("%spattern found, "), msg);
1329 xasprintf (&msg, _("%spattern found, "), msg); 1381 }
1330 result = state_regex; 1382 result = state_regex;
1331 } 1383 } else {
1332 else { 1384 /* FIXME: Shouldn't that be UNKNOWN? */
1333 /* FIXME: Shouldn't that be UNKNOWN? */ 1385 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1334 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 1386 xasprintf(&msg, _("%sExecute Error: %s, "), msg, errbuf);
1335 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf); 1387 result = STATE_CRITICAL;
1336 result = STATE_CRITICAL; 1388 }
1337 } 1389 }
1338 } 1390
1339 1391 /* make sure the page is of an appropriate size */
1340 /* make sure the page is of an appropriate size */ 1392 /* page_len = get_content_length(header); */
1341 /* page_len = get_content_length(header); */ 1393 /* FIXME: Will this work with -N ? IMHO we should use
1342 /* FIXME: Will this work with -N ? IMHO we should use 1394 * get_content_length(header) and always check if it's different than the
1343 * get_content_length(header) and always check if it's different than the 1395 * returned pagesize
1344 * returned pagesize 1396 */
1345 */ 1397 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1346 /* FIXME: IIRC pagesize returns headers - shouldn't we make 1398 * it == get_content_length(header) ??
1347 * it == get_content_length(header) ?? 1399 */
1348 */ 1400 page_len = pagesize;
1349 page_len = pagesize; 1401 if ((max_page_len > 0) && (page_len > max_page_len)) {
1350 if ((max_page_len > 0) && (page_len > max_page_len)) { 1402 xasprintf(&msg, _("%spage size %d too large, "), msg, page_len);
1351 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len); 1403 result = max_state_alt(STATE_WARNING, result);
1352 result = max_state_alt(STATE_WARNING, result); 1404 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1353 } else if ((min_page_len > 0) && (page_len < min_page_len)) { 1405 xasprintf(&msg, _("%spage size %d too small, "), msg, page_len);
1354 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len); 1406 result = max_state_alt(STATE_WARNING, result);
1355 result = max_state_alt(STATE_WARNING, result); 1407 }
1356 } 1408
1357 1409 /* Cut-off trailing characters */
1358 /* Cut-off trailing characters */ 1410 if (msg[strlen(msg) - 2] == ',') {
1359 if(msg[strlen(msg)-2] == ',') 1411 msg[strlen(msg) - 2] = '\0';
1360 msg[strlen(msg)-2] = '\0'; 1412 } else {
1361 else 1413 msg[strlen(msg) - 3] = '\0';
1362 msg[strlen(msg)-3] = '\0'; 1414 }
1363 1415
1364 /* check elapsed time */ 1416 /* check elapsed time */
1365 if (show_extended_perfdata) 1417 if (show_extended_perfdata) {
1366 xasprintf (&msg, 1418 xasprintf(
1367 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), 1419 &msg, _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), msg,
1368 msg, page_len, elapsed_time, 1420 page_len, elapsed_time, (display_html ? "</A>" : ""), perfd_time(elapsed_time),
1369 (display_html ? "</A>" : ""), 1421 perfd_size(page_len), perfd_time_connect(elapsed_time_connect),
1370 perfd_time (elapsed_time), 1422 use_ssl ? perfd_time_ssl(elapsed_time_ssl) : "",
1371 perfd_size (page_len), 1423 perfd_time_headers(elapsed_time_headers), perfd_time_firstbyte(elapsed_time_firstbyte),
1372 perfd_time_connect (elapsed_time_connect), 1424 perfd_time_transfer(elapsed_time_transfer));
1373 use_ssl == true ? perfd_time_ssl (elapsed_time_ssl) : "", 1425 } else {
1374 perfd_time_headers (elapsed_time_headers), 1426 xasprintf(&msg, _("%s - %d bytes in %.3f second response time %s|%s %s"), msg, page_len,
1375 perfd_time_firstbyte (elapsed_time_firstbyte), 1427 elapsed_time, (display_html ? "</A>" : ""), perfd_time(elapsed_time),
1376 perfd_time_transfer (elapsed_time_transfer)); 1428 perfd_size(page_len));
1377 else 1429 }
1378 xasprintf (&msg, 1430
1379 _("%s - %d bytes in %.3f second response time %s|%s %s"), 1431 if (show_body) {
1380 msg, page_len, elapsed_time, 1432 xasprintf(&msg, _("%s\n%s"), msg, page);
1381 (display_html ? "</A>" : ""), 1433 }
1382 perfd_time (elapsed_time), 1434
1383 perfd_size (page_len)); 1435 result = max_state_alt(get_status(elapsed_time, thlds), result);
1384 1436
1385 if (show_body) 1437 die(result, "HTTP %s: %s\n", state_text(result), msg);
1386 xasprintf (&msg, _("%s\n%s"), msg, page); 1438 /* die failed? */
1387 1439 return STATE_UNKNOWN;
1388 result = max_state_alt(get_status(elapsed_time, thlds), result);
1389
1390 die (result, "HTTP %s: %s\n", state_text(result), msg);
1391 /* die failed? */
1392 return STATE_UNKNOWN;
1393} 1440}
1394 1441
1395/* Receivces a pointer to the beginning of the body of a HTTP message 1442/* Receivces a pointer to the beginning of the body of a HTTP message
@@ -1398,94 +1445,95 @@ check_http (void)
1398 * The result must be freed by the caller. 1445 * The result must be freed by the caller.
1399 */ 1446 */
1400char *unchunk_content(const char *content) { 1447char *unchunk_content(const char *content) {
1401 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding 1448 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding
1402 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1 1449 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1
1403 char *result = NULL; 1450 char *result = NULL;
1404 char *start_of_chunk; 1451 char *start_of_chunk;
1405 char* end_of_chunk; 1452 char *end_of_chunk;
1406 long size_of_chunk; 1453 long size_of_chunk;
1407 const char *pointer = content; 1454 const char *pointer = content;
1408 char *endptr; 1455 char *endptr;
1409 long length_of_chunk = 0; 1456 long length_of_chunk = 0;
1410 size_t overall_size = 0; 1457 size_t overall_size = 0;
1411 1458
1412 while (true) { 1459 while (true) {
1413 size_of_chunk = strtol(pointer, &endptr, 16); 1460 size_of_chunk = strtol(pointer, &endptr, 16);
1414 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) { 1461 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) {
1415 // Apparently underflow or overflow, should not happen 1462 // Apparently underflow or overflow, should not happen
1416 if (verbose) { 1463 if (verbose) {
1417 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__); 1464 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__);
1418 } 1465 }
1419 return NULL; 1466 return NULL;
1420 } 1467 }
1421 if (endptr == pointer) { 1468 if (endptr == pointer) {
1422 // Apparently this was not a number 1469 // Apparently this was not a number
1423 if (verbose) { 1470 if (verbose) {
1424 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__); 1471 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__);
1425 } 1472 }
1426 return NULL; 1473 return NULL;
1427 } 1474 }
1428 1475
1429 // So, we got the length of the chunk 1476 // So, we got the length of the chunk
1430 if (*endptr == ';') { 1477 if (*endptr == ';') {
1431 // Chunk extension starts here 1478 // Chunk extension starts here
1432 while (*endptr != '\r') { 1479 while (*endptr != '\r') {
1433 endptr++; 1480 endptr++;
1434 } 1481 }
1435 } 1482 }
1436 1483
1437 start_of_chunk = endptr + 2; 1484 start_of_chunk = endptr + 2;
1438 end_of_chunk = start_of_chunk + size_of_chunk; 1485 end_of_chunk = start_of_chunk + size_of_chunk;
1439 length_of_chunk = (long)(end_of_chunk - start_of_chunk); 1486 length_of_chunk = (long)(end_of_chunk - start_of_chunk);
1440 pointer = end_of_chunk + 2; //Next number should be here 1487 pointer = end_of_chunk + 2; // Next number should be here
1441 1488
1442 if (length_of_chunk == 0) { 1489 if (length_of_chunk == 0) {
1443 // Chunk length is 0, so this is the last one 1490 // Chunk length is 0, so this is the last one
1444 break; 1491 break;
1445 } 1492 }
1446 1493
1447 overall_size += length_of_chunk; 1494 overall_size += length_of_chunk;
1448 1495
1449 if (result == NULL) { 1496 if (result == NULL) {
1450 // Size of the chunk plus the ending NULL byte 1497 // Size of the chunk plus the ending NULL byte
1451 result = (char *)malloc(length_of_chunk +1); 1498 result = (char *)malloc(length_of_chunk + 1);
1452 if (result == NULL) { 1499 if (result == NULL) {
1453 if (verbose) { 1500 if (verbose) {
1454 printf("Failed to allocate memory for unchunked body\n"); 1501 printf("Failed to allocate memory for unchunked body\n");
1455 } 1502 }
1456 return NULL; 1503 return NULL;
1457 } 1504 }
1458 } else { 1505 } else {
1459 // Enlarge memory to the new size plus the ending NULL byte 1506 // Enlarge memory to the new size plus the ending NULL byte
1460 void *tmp = realloc(result, overall_size +1); 1507 void *tmp = realloc(result, overall_size + 1);
1461 if (tmp == NULL) { 1508 if (tmp == NULL) {
1462 if (verbose) { 1509 if (verbose) {
1463 printf("Failed to allocate memory for unchunked body\n"); 1510 printf("Failed to allocate memory for unchunked body\n");
1464 } 1511 }
1465 return NULL; 1512 return NULL;
1466 } else { 1513 }
1467 result = tmp; 1514 result = tmp;
1468 } 1515 }
1469 } 1516
1470 1517 memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk);
1471 memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk); 1518 }
1472 } 1519
1473 1520 if (overall_size == 0 && result == NULL) {
1474 if (overall_size == 0 && result == NULL) { 1521 // We might just have received the end chunk without previous content, so result is never
1475 // We might just have received the end chunk without previous content, so result is never allocated 1522 // allocated
1476 result = calloc(1, sizeof(char)); 1523 result = calloc(1, sizeof(char));
1477 // No error handling here, we can only return NULL anyway 1524 // No error handling here, we can only return NULL anyway
1478 } else { 1525 } else {
1479 result[overall_size] = '\0'; 1526 result[overall_size] = '\0';
1480 } 1527 }
1481 return result; 1528 return result;
1482} 1529}
1483 1530
1484/* per RFC 2396 */ 1531/* per RFC 2396 */
1485#define URI_HTTP "%5[HTPShtps]" 1532#define URI_HTTP "%5[HTPShtps]"
1486#define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" 1533#define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1487#define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */ 1534#define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1488#define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" 1535#define URI_PATH \
1536 "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1489#define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH 1537#define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1490#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH 1538#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1491#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT 1539#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
@@ -1494,414 +1542,431 @@ char *unchunk_content(const char *content) {
1494#define HD5 "//" URI_HOST "/" URI_PATH 1542#define HD5 "//" URI_HOST "/" URI_PATH
1495#define HD6 URI_PATH 1543#define HD6 URI_PATH
1496 1544
1497void 1545void redir(char *pos, char *status_line) {
1498redir (char *pos, char *status_line) 1546 int i = 0;
1499{ 1547 char *x;
1500 int i = 0; 1548 char xx[2];
1501 char *x; 1549 char type[6];
1502 char xx[2]; 1550 char *addr;
1503 char type[6]; 1551 char *url;
1504 char *addr; 1552
1505 char *url; 1553 addr = malloc(MAX_IPV4_HOSTLENGTH + 1);
1506 1554 if (addr == NULL) {
1507 addr = malloc (MAX_IPV4_HOSTLENGTH + 1); 1555 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1508 if (addr == NULL) 1556 }
1509 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); 1557
1510 1558 memset(addr, 0, MAX_IPV4_HOSTLENGTH);
1511 memset(addr, 0, MAX_IPV4_HOSTLENGTH); 1559 url = malloc(strcspn(pos, "\r\n"));
1512 url = malloc (strcspn (pos, "\r\n")); 1560 if (url == NULL) {
1513 if (url == NULL) 1561 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1514 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 1562 }
1515 1563
1516 while (pos) { 1564 while (pos) {
1517 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i); 1565 sscanf(pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1518 if (i == 0) { 1566 if (i == 0) {
1519 pos += (size_t) strcspn (pos, "\r\n"); 1567 pos += (size_t)strcspn(pos, "\r\n");
1520 pos += (size_t) strspn (pos, "\r\n"); 1568 pos += (size_t)strspn(pos, "\r\n");
1521 if (strlen(pos) == 0) 1569 if (strlen(pos) == 0) {
1522 die (STATE_UNKNOWN, 1570 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1523 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"), 1571 status_line, (display_html ? "</A>" : ""));
1524 status_line, (display_html ? "</A>" : "")); 1572 }
1525 continue; 1573 continue;
1526 } 1574 }
1527 1575
1528 pos += i; 1576 pos += i;
1529 pos += strspn (pos, " \t"); 1577 pos += strspn(pos, " \t");
1530 1578
1531 /* 1579 /*
1532 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by 1580 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1533 * preceding each extra line with at least one SP or HT.'' 1581 * preceding each extra line with at least one SP or HT.''
1534 */ 1582 */
1535 for (; (i = strspn (pos, "\r\n")); pos += i) { 1583 for (; (i = strspn(pos, "\r\n")); pos += i) {
1536 pos += i; 1584 pos += i;
1537 if (!(i = strspn (pos, " \t"))) { 1585 if (!(i = strspn(pos, " \t"))) {
1538 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"), 1586 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1539 display_html ? "</A>" : ""); 1587 display_html ? "</A>" : "");
1540 } 1588 }
1541 } 1589 }
1542 1590
1543 url = realloc (url, strcspn (pos, "\r\n") + 1); 1591 url = realloc(url, strcspn(pos, "\r\n") + 1);
1544 if (url == NULL) 1592 if (url == NULL) {
1545 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); 1593 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1546 1594 }
1547 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ 1595
1548 if (sscanf (pos, HD1, type, addr, &i, url) == 4) { 1596 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1549 url = prepend_slash (url); 1597 if (sscanf(pos, HD1, type, addr, &i, url) == 4) {
1550 use_ssl = server_type_check (type); 1598 url = prepend_slash(url);
1551 } 1599 use_ssl = server_type_check(type);
1552 1600 }
1553 /* URI_HTTP URI_HOST URI_PATH */ 1601
1554 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 1602 /* URI_HTTP URI_HOST URI_PATH */
1555 url = prepend_slash (url); 1603 else if (sscanf(pos, HD2, type, addr, url) == 3) {
1556 use_ssl = server_type_check (type); 1604 url = prepend_slash(url);
1557 i = server_port_check (use_ssl); 1605 use_ssl = server_type_check(type);
1558 } 1606 i = server_port_check(use_ssl);
1559 1607 }
1560 /* URI_HTTP URI_HOST URI_PORT */ 1608
1561 else if (sscanf (pos, HD3, type, addr, &i) == 3) { 1609 /* URI_HTTP URI_HOST URI_PORT */
1562 strcpy (url, HTTP_URL); 1610 else if (sscanf(pos, HD3, type, addr, &i) == 3) {
1563 use_ssl = server_type_check (type); 1611 strcpy(url, HTTP_URL);
1564 } 1612 use_ssl = server_type_check(type);
1565 1613 }
1566 /* URI_HTTP URI_HOST */ 1614
1567 else if (sscanf (pos, HD4, type, addr) == 2) { 1615 /* URI_HTTP URI_HOST */
1568 strcpy (url, HTTP_URL); 1616 else if (sscanf(pos, HD4, type, addr) == 2) {
1569 use_ssl = server_type_check (type); 1617 strcpy(url, HTTP_URL);
1570 i = server_port_check (use_ssl); 1618 use_ssl = server_type_check(type);
1571 } 1619 i = server_port_check(use_ssl);
1572 /* URI_HTTP, URI_HOST, URI_PATH */ 1620 }
1573 else if (sscanf (pos, HD5, addr, url) == 2) { 1621 /* URI_HTTP, URI_HOST, URI_PATH */
1574 if(use_ssl){ 1622 else if (sscanf(pos, HD5, addr, url) == 2) {
1575 strcpy (type,"https"); 1623 if (use_ssl) {
1576 } 1624 strcpy(type, "https");
1577 else{ 1625 } else {
1578 strcpy (type, server_type); 1626 strcpy(type, server_type);
1579 } 1627 }
1580 xasprintf (&url, "/%s", url); 1628 xasprintf(&url, "/%s", url);
1581 use_ssl = server_type_check (type); 1629 use_ssl = server_type_check(type);
1582 i = server_port_check (use_ssl); 1630 i = server_port_check(use_ssl);
1583 } 1631 }
1584 1632
1585 /* URI_PATH */ 1633 /* URI_PATH */
1586 else if (sscanf (pos, HD6, url) == 1) { 1634 else if (sscanf(pos, HD6, url) == 1) {
1587 /* relative url */ 1635 /* relative url */
1588 if ((url[0] != '/')) { 1636 if ((url[0] != '/')) {
1589 if ((x = strrchr(server_url, '/'))) 1637 if ((x = strrchr(server_url, '/'))) {
1590 *x = '\0'; 1638 *x = '\0';
1591 xasprintf (&url, "%s/%s", server_url, url); 1639 }
1592 } 1640 xasprintf(&url, "%s/%s", server_url, url);
1593 i = server_port; 1641 }
1594 strcpy (type, server_type); 1642 i = server_port;
1595 strcpy (addr, host_name ? host_name : server_address); 1643 strcpy(type, server_type);
1596 } 1644 strcpy(addr, host_name ? host_name : server_address);
1597 1645 }
1598 else { 1646
1599 die (STATE_UNKNOWN, 1647 else {
1600 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), 1648 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), pos,
1601 pos, (display_html ? "</A>" : "")); 1649 (display_html ? "</A>" : ""));
1602 } 1650 }
1603 1651
1604 break; 1652 break;
1605 1653
1606 } /* end while (pos) */ 1654 } /* end while (pos) */
1607 1655
1608 if (++redir_depth > max_depth) 1656 if (++redir_depth > max_depth) {
1609 die (STATE_WARNING, 1657 die(STATE_WARNING,
1610 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), 1658 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), max_depth,
1611 max_depth, type, addr, i, url, (display_html ? "</A>" : "")); 1659 type, addr, i, url, (display_html ? "</A>" : ""));
1612 1660 }
1613 if (server_port==i && 1661
1614 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) && 1662 if (server_port == i && !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1615 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && 1663 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, url)) {
1616 !strcmp(server_url, url)) 1664 die(STATE_CRITICAL,
1617 die (STATE_CRITICAL, 1665 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), type,
1618 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), 1666 addr, i, url, (display_html ? "</A>" : ""));
1619 type, addr, i, url, (display_html ? "</A>" : "")); 1667 }
1620 1668
1621 strcpy (server_type, type); 1669 strcpy(server_type, type);
1622 1670
1623 free (host_name); 1671 free(host_name);
1624 host_name = strndup (addr, MAX_IPV4_HOSTLENGTH); 1672 host_name = strndup(addr, MAX_IPV4_HOSTLENGTH);
1625 1673
1626 if (!(followsticky & STICKY_HOST)) { 1674 if (!(followsticky & STICKY_HOST)) {
1627 free (server_address); 1675 free(server_address);
1628 server_address = strndup (addr, MAX_IPV4_HOSTLENGTH); 1676 server_address = strndup(addr, MAX_IPV4_HOSTLENGTH);
1629 } 1677 }
1630 if (!(followsticky & STICKY_PORT)) { 1678 if (!(followsticky & STICKY_PORT)) {
1631 server_port = i; 1679 server_port = i;
1632 } 1680 }
1633 1681
1634 free (server_url); 1682 free(server_url);
1635 server_url = url; 1683 server_url = url;
1636 1684
1637 if (server_port > MAX_PORT) 1685 if (server_port > MAX_PORT) {
1638 die (STATE_UNKNOWN, 1686 die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1639 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"), 1687 MAX_PORT, server_type, server_address, server_port, server_url,
1640 MAX_PORT, server_type, server_address, server_port, server_url, 1688 display_html ? "</A>" : "");
1641 display_html ? "</A>" : ""); 1689 }
1642
1643 /* reset virtual port */
1644 virtual_port = server_port;
1645
1646 if (verbose)
1647 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1648 host_name ? host_name : server_address, server_port, server_url);
1649
1650 free(addr);
1651 check_http ();
1652}
1653 1690
1691 /* reset virtual port */
1692 virtual_port = server_port;
1654 1693
1655bool 1694 if (verbose) {
1656server_type_check (const char *type) 1695 printf(_("Redirection to %s://%s:%d%s\n"), server_type,
1657{ 1696 host_name ? host_name : server_address, server_port, server_url);
1658 if (strcmp (type, "https")) 1697 }
1659 return false; 1698
1660 else 1699 free(addr);
1661 return true; 1700 check_http();
1662} 1701}
1663 1702
1664int 1703bool server_type_check(const char *type) { return (!(bool)strcmp(type, "https")); }
1665server_port_check (int ssl_flag) 1704
1666{ 1705int server_port_check(int ssl_flag) {
1667 if (ssl_flag) 1706 if (ssl_flag) {
1668 return HTTPS_PORT; 1707 return HTTPS_PORT;
1669 else 1708 }
1670 return HTTP_PORT; 1709 return HTTP_PORT;
1671} 1710}
1672 1711
1673char *perfd_time (double elapsed_time) 1712char *perfd_time(double elapsed_time) {
1674{ 1713 return fperfdata("time", elapsed_time, "s", thlds->warning,
1675 return fperfdata ("time", elapsed_time, "s", 1714 thlds->warning ? thlds->warning->end : 0, thlds->critical,
1676 thlds->warning?true:false, thlds->warning?thlds->warning->end:0, 1715 thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout);
1677 thlds->critical?true:false, thlds->critical?thlds->critical->end:0,
1678 true, 0, true, socket_timeout);
1679} 1716}
1680 1717
1681char *perfd_time_connect (double elapsed_time_connect) 1718char *perfd_time_connect(double elapsed_time_connect) {
1682{ 1719 return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true,
1683 return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1720 socket_timeout);
1684} 1721}
1685 1722
1686char *perfd_time_ssl (double elapsed_time_ssl) 1723char *perfd_time_ssl(double elapsed_time_ssl) {
1687{ 1724 return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true,
1688 return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1725 socket_timeout);
1689} 1726}
1690 1727
1691char *perfd_time_headers (double elapsed_time_headers) 1728char *perfd_time_headers(double elapsed_time_headers) {
1692{ 1729 return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true,
1693 return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1730 socket_timeout);
1694} 1731}
1695 1732
1696char *perfd_time_firstbyte (double elapsed_time_firstbyte) 1733char *perfd_time_firstbyte(double elapsed_time_firstbyte) {
1697{ 1734 return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0,
1698 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1735 true, socket_timeout);
1699} 1736}
1700 1737
1701char *perfd_time_transfer (double elapsed_time_transfer) 1738char *perfd_time_transfer(double elapsed_time_transfer) {
1702{ 1739 return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0,
1703 return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); 1740 true, socket_timeout);
1704} 1741}
1705 1742
1706char *perfd_size (int page_len) 1743char *perfd_size(int page_len) {
1707{ 1744 return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0,
1708 return perfdata ("size", page_len, "B", 1745 true, 0, false, 0);
1709 (min_page_len>0?true:false), min_page_len,
1710 (min_page_len>0?true:false), 0,
1711 true, 0, false, 0);
1712} 1746}
1713 1747
1714void 1748void print_help(void) {
1715print_help (void) 1749 print_revision(progname, NP_VERSION);
1716{
1717 print_revision (progname, NP_VERSION);
1718 1750
1719 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 1751 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1720 printf (COPYRIGHT, copyright, email); 1752 printf(COPYRIGHT, copyright, email);
1721 1753
1722 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); 1754 printf("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1723 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); 1755 printf("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1724 printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); 1756 printf("%s\n", _("strings and regular expressions, check connection times, and report on"));
1725 printf ("%s\n", _("certificate expiration times.")); 1757 printf("%s\n", _("certificate expiration times."));
1726 1758
1727 printf ("\n\n"); 1759 printf("\n");
1760 printf("%s\n", _("ATTENTION!"));
1761 printf("\n");
1762 printf("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the"));
1763 printf("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should"));
1764 printf("%s\n", _("migrate your checks over to check_curl, because check_http is going to be"));
1765 printf("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your"));
1766 printf("%s\n", _("check command definitions."));
1767 printf("%s\n",
1768 _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues"));
1728 1769
1729 print_usage (); 1770 printf("\n\n");
1771
1772 print_usage();
1730 1773
1731#ifdef HAVE_SSL 1774#ifdef HAVE_SSL
1732 printf (_("In the first form, make an HTTP request.")); 1775 printf(_("In the first form, make an HTTP request."));
1733 printf (_("In the second form, connect to the server and check the TLS certificate.")); 1776 printf(_("In the second form, connect to the server and check the TLS certificate."));
1734#endif 1777#endif
1735 printf (_("NOTE: One or both of -H and -I must be specified")); 1778 printf(_("NOTE: One or both of -H and -I must be specified"));
1736 1779
1737 printf ("\n"); 1780 printf("\n");
1738 1781
1739 printf (UT_HELP_VRSN); 1782 printf(UT_HELP_VRSN);
1740 printf (UT_EXTRA_OPTS); 1783 printf(UT_EXTRA_OPTS);
1741 1784
1742 printf (" %s\n", "-H, --hostname=ADDRESS"); 1785 printf(" %s\n", "-H, --hostname=ADDRESS");
1743 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); 1786 printf(" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1744 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); 1787 printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1745 printf (" %s\n", "-I, --IP-address=ADDRESS"); 1788 printf(" %s\n", "-I, --IP-address=ADDRESS");
1746 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); 1789 printf(" %s\n",
1747 printf (" %s\n", "-p, --port=INTEGER"); 1790 _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1748 printf (" %s", _("Port number (default: ")); 1791 printf(" %s\n", "-p, --port=INTEGER");
1749 printf ("%d)\n", HTTP_PORT); 1792 printf(" %s", _("Port number (default: "));
1793 printf("%d)\n", HTTP_PORT);
1750 1794
1751 printf (UT_IPv46); 1795 printf(UT_IPv46);
1752 1796
1753#ifdef HAVE_SSL 1797#ifdef HAVE_SSL
1754 printf (" %s\n", "-S, --ssl=VERSION[+]"); 1798 printf(" %s\n", "-S, --ssl=VERSION[+]");
1755 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); 1799 printf(" %s\n",
1756 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); 1800 _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1757 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted.")); 1801 printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1758 printf (" %s\n", "--sni"); 1802 printf(" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1759 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1803 printf(" %s\n", "--sni");
1760 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1804 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1761 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443")); 1805 printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1762 printf (" %s\n", _("(when this option is used the URL is not checked by default. You can use")); 1806 printf(" %s\n",
1763 printf (" %s\n", _(" --continue-after-certificate to override this behavior)")); 1807 _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1764 printf (" %s\n", "--continue-after-certificate"); 1808 printf(" %s\n",
1765 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); 1809 _("(when this option is used the URL is not checked by default. You can use"));
1766 printf (" %s\n", _("Does nothing unless -C is used.")); 1810 printf(" %s\n", _(" --continue-after-certificate to override this behavior)"));
1767 printf (" %s\n", "-J, --client-cert=FILE"); 1811 printf(" %s\n", "--continue-after-certificate");
1768 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1812 printf(" %s\n",
1769 printf (" %s\n", _("to be used in establishing the SSL session")); 1813 _("Allows the HTTP check to continue after performing the certificate check."));
1770 printf (" %s\n", "-K, --private-key=FILE"); 1814 printf(" %s\n", _("Does nothing unless -C is used."));
1771 printf (" %s\n", _("Name of file containing the private key (PEM format)")); 1815 printf(" %s\n", "-J, --client-cert=FILE");
1772 printf (" %s\n", _("matching the client certificate")); 1816 printf(" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1817 printf(" %s\n", _("to be used in establishing the SSL session"));
1818 printf(" %s\n", "-K, --private-key=FILE");
1819 printf(" %s\n", _("Name of file containing the private key (PEM format)"));
1820 printf(" %s\n", _("matching the client certificate"));
1773#endif 1821#endif
1774 1822
1775 printf (" %s\n", "-e, --expect=STRING"); 1823 printf(" %s\n", "-e, --expect=STRING");
1776 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); 1824 printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1777 printf (" %s", _("the first (status) line of the server response (default: ")); 1825 printf(" %s", _("the first (status) line of the server response (default: "));
1778 printf ("%s)\n", HTTP_EXPECT); 1826 printf("%s)\n", HTTP_EXPECT);
1779 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); 1827 printf(" %s\n",
1780 printf (" %s\n", "-d, --header-string=STRING"); 1828 _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1781 printf (" %s\n", _("String to expect in the response headers")); 1829 printf(" %s\n", "-d, --header-string=STRING");
1782 printf (" %s\n", "-s, --string=STRING"); 1830 printf(" %s\n", _("String to expect in the response headers"));
1783 printf (" %s\n", _("String to expect in the content")); 1831 printf(" %s\n", "-s, --string=STRING");
1784 printf (" %s\n", "-u, --url=PATH"); 1832 printf(" %s\n", _("String to expect in the content"));
1785 printf (" %s\n", _("URL to GET or POST (default: /)")); 1833 printf(" %s\n", "-u, --url=PATH");
1786 printf (" %s\n", "-P, --post=STRING"); 1834 printf(" %s\n", _("URL to GET or POST (default: /)"));
1787 printf (" %s\n", _("URL decoded http POST data")); 1835 printf(" %s\n", "-P, --post=STRING");
1788 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT, CONNECT:POST)"); 1836 printf(" %s\n", _("URL decoded http POST data"));
1789 printf (" %s\n", _("Set HTTP method.")); 1837 printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, "
1790 printf (" %s\n", "-N, --no-body"); 1838 "CONNECT, CONNECT:POST)");
1791 printf (" %s\n", _("Don't wait for document body: stop reading after headers.")); 1839 printf(" %s\n", _("Set HTTP method."));
1792 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); 1840 printf(" %s\n", "-N, --no-body");
1793 printf (" %s\n", "-M, --max-age=SECONDS"); 1841 printf(" %s\n", _("Don't wait for document body: stop reading after headers."));
1794 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); 1842 printf(" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1795 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); 1843 printf(" %s\n", "-M, --max-age=SECONDS");
1796 printf (" %s\n", "-T, --content-type=STRING"); 1844 printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1797 printf (" %s\n", _("specify Content-Type header media type when POSTing\n")); 1845 printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1798 1846 printf(" %s\n", "-T, --content-type=STRING");
1799 printf (" %s\n", "-l, --linespan"); 1847 printf(" %s\n", _("specify Content-Type header media type when POSTing\n"));
1800 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); 1848
1801 printf (" %s\n", "-r, --regex, --ereg=STRING"); 1849 printf(" %s\n", "-l, --linespan");
1802 printf (" %s\n", _("Search page for regex STRING")); 1850 printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1803 printf (" %s\n", "-R, --eregi=STRING"); 1851 printf(" %s\n", "-r, --regex, --ereg=STRING");
1804 printf (" %s\n", _("Search page for case-insensitive regex STRING")); 1852 printf(" %s\n", _("Search page for regex STRING"));
1805 printf (" %s\n", "--invert-regex"); 1853 printf(" %s\n", "-R, --eregi=STRING");
1806 printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); 1854 printf(" %s\n", _("Search page for case-insensitive regex STRING"));
1807 printf (" %s\n", _("can be changed with --state--regex)")); 1855 printf(" %s\n", "--invert-regex");
1808 printf (" %s\n", "--state-regex=STATE"); 1856 printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1809 printf (" %s\n", _("Return STATE if regex is found, OK if not\n")); 1857 printf(" %s\n", _("can be changed with --state--regex)"));
1810 1858 printf(" %s\n", "--state-regex=STATE");
1811 printf (" %s\n", "-a, --authorization=AUTH_PAIR"); 1859 printf(" %s\n", _("Return STATE if regex is found, OK if not\n"));
1812 printf (" %s\n", _("Username:password on sites with basic authentication")); 1860
1813 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1861 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1814 printf (" %s\n", _("Username:password on proxy-servers with basic authentication")); 1862 printf(" %s\n", _("Username:password on sites with basic authentication"));
1815 printf (" %s\n", "-A, --useragent=STRING"); 1863 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1816 printf (" %s\n", _("String to be sent in http header as \"User Agent\"")); 1864 printf(" %s\n", _("Username:password on proxy-servers with basic authentication"));
1817 printf (" %s\n", "-k, --header=STRING"); 1865 printf(" %s\n", "-A, --useragent=STRING");
1818 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); 1866 printf(" %s\n", _("String to be sent in http header as \"User Agent\""));
1819 printf (" %s\n", "-E, --extended-perfdata"); 1867 printf(" %s\n", "-k, --header=STRING");
1820 printf (" %s\n", _("Print additional performance data")); 1868 printf(
1821 printf (" %s\n", "-B, --show-body"); 1869 " %s\n",
1822 printf (" %s\n", _("Print body content below status line")); 1870 _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1823 printf (" %s\n", "-L, --link"); 1871 printf(" %s\n", "-E, --extended-perfdata");
1824 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1872 printf(" %s\n", _("Print additional performance data"));
1825 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>"); 1873 printf(" %s\n", "-B, --show-body");
1826 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1874 printf(" %s\n", _("Print body content below status line"));
1827 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1875 printf(" %s\n", "-L, --link");
1828 printf (" %s\n", "--max-redirs=INTEGER"); 1876 printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1829 printf (" %s", _("Maximal number of redirects (default: ")); 1877 printf(" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1830 printf ("%d)\n", DEFAULT_MAX_REDIRS); 1878 printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1831 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1879 printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1832 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1880 printf(" %s\n", "--max-redirs=INTEGER");
1833 printf (UT_WARN_CRIT); 1881 printf(" %s", _("Maximal number of redirects (default: "));
1834 1882 printf("%d)\n", DEFAULT_MAX_REDIRS);
1835 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1883 printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1836 1884 printf(" %s\n",
1837 printf (UT_VERBOSE); 1885 _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1838 1886 printf(UT_WARN_CRIT);
1839 printf ("\n"); 1887
1840 printf ("%s\n", _("Notes:")); 1888 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1841 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); 1889
1842 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); 1890 printf(UT_VERBOSE);
1843 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); 1891
1844 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); 1892 printf("\n");
1845 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); 1893 printf("%s\n", _("Notes:"));
1846 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); 1894 printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1895 printf(" %s\n",
1896 _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1897 printf(" %s\n",
1898 _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1899 printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1900 printf(" %s\n",
1901 _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1902 printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1847 1903
1848#ifdef HAVE_SSL 1904#ifdef HAVE_SSL
1849 printf ("\n"); 1905 printf("\n");
1850 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to")); 1906 printf(" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1851 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 ")); 1907 printf(" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1852 printf (" %s\n", _("certificate is still valid for the specified number of days.")); 1908 printf(" %s\n", _("certificate is still valid for the specified number of days."));
1853 printf ("\n"); 1909 printf("\n");
1854 printf (" %s\n", _("Please note that this plugin does not check if the presented server")); 1910 printf(" %s\n", _("Please note that this plugin does not check if the presented server"));
1855 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate")); 1911 printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1856 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); 1912 printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1857 printf ("\n"); 1913 printf("\n");
1858 printf ("%s\n", _("Examples:")); 1914 printf("%s\n", _("Examples:"));
1859 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com"); 1915 printf(" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1860 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); 1916 printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1861 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1917 printf(" %s\n",
1862 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1918 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1863 printf (" %s\n", _("a STATE_CRITICAL will be returned.")); 1919 printf(" %s\n",
1864 printf ("\n"); 1920 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1865 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14"); 1921 printf(" %s\n", _("a STATE_CRITICAL will be returned."));
1866 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); 1922 printf("\n");
1867 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1923 printf(" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1868 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); 1924 printf(" %s\n",
1869 printf (" %s\n\n", _("the certificate is expired.")); 1925 _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1870 printf ("\n"); 1926 printf(" %s\n",
1871 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14"); 1927 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1872 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); 1928 printf(" %s\n",
1873 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); 1929 _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1874 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); 1930 printf(" %s\n\n", _("the certificate is expired."));
1875 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); 1931 printf("\n");
1876 1932 printf(" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1877 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); 1933 printf(" %s\n",
1878 printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); 1934 _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1879 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>")); 1935 printf(" %s\n",
1880 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); 1936 _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1881 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); 1937 printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1882 printf (" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can set the method used")); 1938 printf(" %s\n",
1883 printf (" %s\n", _("inside the proxied connection: -j CONNECT:POST")); 1939 _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1940
1941 printf(" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1942 printf(" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j "
1943 "CONNECT -H www.verisign.com "));
1944 printf(" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> "
1945 "-S(sl) -j CONNECT -H <webserver>"));
1946 printf(" %s\n",
1947 _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1948 printf(" %s\n",
1949 _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1950 printf(" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can "
1951 "set the method used"));
1952 printf(" %s\n", _("inside the proxied connection: -j CONNECT:POST"));
1884 1953
1885#endif 1954#endif
1886 1955
1887 printf (UT_SUPPORT); 1956 printf(UT_SUPPORT);
1888
1889} 1957}
1890 1958
1891 1959void print_usage(void) {
1892 1960 printf("%s\n", _("Usage:"));
1893void 1961 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
1894print_usage (void) 1962 printf(" [-J <client certificate file>] [-K <private key>]\n");
1895{ 1963 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1896 printf ("%s\n", _("Usage:")); 1964 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n");
1897 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname); 1965 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1898 printf (" [-J <client certificate file>] [-K <private key>]\n"); 1966 "regex>]\n");
1899 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1967 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1900 printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n"); 1968 printf(" [-A string] [-k string] [-S <version>] [--sni]\n");
1901 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1969 printf(" [-T <content-type>] [-j method]\n");
1902 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1970 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
1903 printf (" [-A string] [-k string] [-S <version>] [--sni]\n"); 1971 printf(" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1904 printf (" [-T <content-type>] [-j method]\n");
1905 printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
1906 printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1907} 1972}
diff --git a/plugins/check_ide_smart.c b/plugins/check_ide_smart.c
index 9640ef70..16fe3d01 100644
--- a/plugins/check_ide_smart.c
+++ b/plugins/check_ide_smart.c
@@ -56,7 +56,6 @@ void print_usage(void);
56# include <sys/device.h> 56# include <sys/device.h>
57# include <sys/param.h> 57# include <sys/param.h>
58# include <sys/sysctl.h> 58# include <sys/sysctl.h>
59# include <sys/videoio.h> /* for __u8 and friends */
60# include <sys/scsiio.h> 59# include <sys/scsiio.h>
61# include <sys/ataio.h> 60# include <sys/ataio.h>
62# include <dev/ata/atareg.h> 61# include <dev/ata/atareg.h>
@@ -79,48 +78,47 @@ void print_usage(void);
79#define UNKNOWN -1 78#define UNKNOWN -1
80 79
81typedef struct threshold_s { 80typedef struct threshold_s {
82 __u8 id; 81 uint8_t id;
83 __u8 threshold; 82 uint8_t threshold;
84 __u8 reserved[10]; 83 uint8_t reserved[10];
85} __attribute__((packed)) threshold_t; 84} __attribute__((packed)) threshold_t;
86 85
87typedef struct thresholds_s { 86typedef struct thresholds_s {
88 __u16 revision; 87 uint16_t revision;
89 threshold_t thresholds[NR_ATTRIBUTES]; 88 threshold_t thresholds[NR_ATTRIBUTES];
90 __u8 reserved[18]; 89 uint8_t reserved[18];
91 __u8 vendor[131]; 90 uint8_t vendor[131];
92 __u8 checksum; 91 uint8_t checksum;
93} __attribute__((packed)) thresholds_t; 92} __attribute__((packed)) thresholds_t;
94 93
95typedef struct value_s { 94typedef struct value_s {
96 __u8 id; 95 uint8_t id;
97 __u16 status; 96 uint16_t status;
98 __u8 value; 97 uint8_t value;
99 __u8 vendor[8]; 98 uint8_t vendor[8];
100} __attribute__((packed)) value_t; 99} __attribute__((packed)) value_t;
101 100
102typedef struct values_s { 101typedef struct values_s {
103 __u16 revision; 102 uint16_t revision;
104 value_t values[NR_ATTRIBUTES]; 103 value_t values[NR_ATTRIBUTES];
105 __u8 offline_status; 104 uint8_t offline_status;
106 __u8 vendor1; 105 uint8_t vendor1;
107 __u16 offline_timeout; 106 uint16_t offline_timeout;
108 __u8 vendor2; 107 uint8_t vendor2;
109 __u8 offline_capability; 108 uint8_t offline_capability;
110 __u16 smart_capability; 109 uint16_t smart_capability;
111 __u8 reserved[16]; 110 uint8_t reserved[16];
112 __u8 vendor[125]; 111 uint8_t vendor[125];
113 __u8 checksum; 112 uint8_t checksum;
114} __attribute__((packed)) values_t; 113} __attribute__((packed)) values_t;
115 114
116static struct { 115static struct {
117 __u8 value; 116 uint8_t value;
118 char *text; 117 char *text;
119} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, 118} offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"}, {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
120 {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
121 119
122static struct { 120static struct {
123 __u8 value; 121 uint8_t value;
124 char *text; 122 char *text;
125} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"}, 123} smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"},
126 {SMART_DISABLE, "SMART_DISABLE"}, 124 {SMART_DISABLE, "SMART_DISABLE"},
@@ -140,7 +138,7 @@ static int smart_read_values(int, values_t *);
140static int nagios(values_t *, thresholds_t *); 138static int nagios(values_t *, thresholds_t *);
141static void print_value(value_t *, threshold_t *); 139static void print_value(value_t *, threshold_t *);
142static void print_values(values_t *, thresholds_t *); 140static void print_values(values_t *, thresholds_t *);
143static int smart_cmd_simple(int, enum SmartCommand, __u8, bool); 141static int smart_cmd_simple(int, enum SmartCommand, uint8_t, bool);
144static int smart_read_thresholds(int, thresholds_t *); 142static int smart_read_thresholds(int, thresholds_t *);
145static bool verbose = false; 143static bool verbose = false;
146 144
@@ -175,8 +173,9 @@ int main(int argc, char *argv[]) {
175 173
176 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex); 174 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
177 175
178 if (o == -1 || o == EOF || o == 1) 176 if (o == -1 || o == EOF || o == 1) {
179 break; 177 break;
178 }
180 179
181 switch (o) { 180 switch (o) {
182 case 'd': 181 case 'd':
@@ -234,8 +233,9 @@ int main(int argc, char *argv[]) {
234 smart_read_values(fd, &values); 233 smart_read_values(fd, &values);
235 smart_read_thresholds(fd, &thresholds); 234 smart_read_thresholds(fd, &thresholds);
236 retval = nagios(&values, &thresholds); 235 retval = nagios(&values, &thresholds);
237 if (verbose) 236 if (verbose) {
238 print_values(&values, &thresholds); 237 print_values(&values, &thresholds);
238 }
239 239
240 close(fd); 240 close(fd);
241 return retval; 241 return retval;
@@ -254,7 +254,7 @@ char *get_offline_text(int status) {
254int smart_read_values(int fd, values_t *values) { 254int smart_read_values(int fd, values_t *values) {
255#ifdef __linux__ 255#ifdef __linux__
256 int e; 256 int e;
257 __u8 args[4 + 512]; 257 uint8_t args[4 + 512];
258 args[0] = WIN_SMART; 258 args[0] = WIN_SMART;
259 args[1] = 0; 259 args[1] = 0;
260 args[2] = SMART_READ_VALUES; 260 args[2] = SMART_READ_VALUES;
@@ -282,8 +282,9 @@ int smart_read_values(int fd, values_t *values) {
282 req.cylinder = WDSMART_CYL; 282 req.cylinder = WDSMART_CYL;
283 283
284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
285 if (req.retsts != ATACMD_OK) 285 if (req.retsts != ATACMD_OK) {
286 errno = ENODEV; 286 errno = ENODEV;
287 }
287 } 288 }
288 289
289 if (errno != 0) { 290 if (errno != 0) {
@@ -370,22 +371,24 @@ void print_values(values_t *p, thresholds_t *t) {
370 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : ""); 371 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : "");
371} 372}
372 373
373int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_error) { 374int smart_cmd_simple(int fd, enum SmartCommand command, uint8_t val0, bool show_error) {
374 int e = STATE_UNKNOWN; 375 int e = STATE_UNKNOWN;
375#ifdef __linux__ 376#ifdef __linux__
376 __u8 args[4]; 377 uint8_t args[4];
377 args[0] = WIN_SMART; 378 args[0] = WIN_SMART;
378 args[1] = val0; 379 args[1] = val0;
379 args[2] = smart_command[command].value; 380 args[2] = smart_command[command].value;
380 args[3] = 0; 381 args[3] = 0;
381 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { 382 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
382 e = STATE_CRITICAL; 383 e = STATE_CRITICAL;
383 if (show_error) 384 if (show_error) {
384 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 385 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
386 }
385 } else { 387 } else {
386 e = STATE_OK; 388 e = STATE_OK;
387 if (show_error) 389 if (show_error) {
388 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 390 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
391 }
389 } 392 }
390 393
391#endif /* __linux__ */ 394#endif /* __linux__ */
@@ -401,20 +404,24 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err
401 req.sec_count = val0; 404 req.sec_count = val0;
402 405
403 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 406 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
404 if (req.retsts != ATACMD_OK) 407 if (req.retsts != ATACMD_OK) {
405 errno = ENODEV; 408 errno = ENODEV;
406 if (req.cylinder != WDSMART_CYL) 409 }
410 if (req.cylinder != WDSMART_CYL) {
407 errno = ENODEV; 411 errno = ENODEV;
412 }
408 } 413 }
409 414
410 if (errno != 0) { 415 if (errno != 0) {
411 e = STATE_CRITICAL; 416 e = STATE_CRITICAL;
412 if (show_error) 417 if (show_error) {
413 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno)); 418 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
419 }
414 } else { 420 } else {
415 e = STATE_OK; 421 e = STATE_OK;
416 if (show_error) 422 if (show_error) {
417 printf(_("OK - Command sent (%s)\n"), smart_command[command].text); 423 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
424 }
418 } 425 }
419 426
420#endif /* __NetBSD__ */ 427#endif /* __NetBSD__ */
@@ -424,7 +431,7 @@ int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_err
424int smart_read_thresholds(int fd, thresholds_t *thresholds) { 431int smart_read_thresholds(int fd, thresholds_t *thresholds) {
425#ifdef __linux__ 432#ifdef __linux__
426 int e; 433 int e;
427 __u8 args[4 + 512]; 434 uint8_t args[4 + 512];
428 args[0] = WIN_SMART; 435 args[0] = WIN_SMART;
429 args[1] = 0; 436 args[1] = 0;
430 args[2] = SMART_READ_THRESHOLDS; 437 args[2] = SMART_READ_THRESHOLDS;
@@ -452,8 +459,9 @@ int smart_read_thresholds(int fd, thresholds_t *thresholds) {
452 req.cylinder = WDSMART_CYL; 459 req.cylinder = WDSMART_CYL;
453 460
454 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) { 461 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
455 if (req.retsts != ATACMD_OK) 462 if (req.retsts != ATACMD_OK) {
456 errno = ENODEV; 463 errno = ENODEV;
464 }
457 } 465 }
458 466
459 if (errno != 0) { 467 if (errno != 0) {
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c
index 87818da6..77a33304 100644
--- a/plugins/check_ldap.c
+++ b/plugins/check_ldap.c
@@ -1,30 +1,30 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ldap plugin 3 * Monitoring check_ldap plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_ldap plugin 10 * This file contains the check_ldap plugin
11* 11 *
12* 12 *
13* This program is free software: you can redistribute it and/or modify 13 * This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14 * it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15 * the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16 * (at your option) any later version.
17* 17 *
18* This program is distributed in the hope that it will be useful, 18 * This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21 * GNU General Public License for more details.
22* 22 *
23* You should have received a copy of the GNU General Public License 23 * You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25 *
26* 26 *
27*****************************************************************************/ 27 *****************************************************************************/
28 28
29/* progname may be check_ldaps */ 29/* progname may be check_ldaps */
30char *progname = "check_ldap"; 30char *progname = "check_ldap";
@@ -34,209 +34,183 @@ const char *email = "devel@monitoring-plugins.org";
34#include "common.h" 34#include "common.h"
35#include "netutils.h" 35#include "netutils.h"
36#include "utils.h" 36#include "utils.h"
37#include "check_ldap.d/config.h"
37 38
39#include "states.h"
38#include <lber.h> 40#include <lber.h>
39#define LDAP_DEPRECATED 1 41#define LDAP_DEPRECATED 1
40#include <ldap.h> 42#include <ldap.h>
41 43
42enum { 44enum {
43 UNDEFINED = 0,
44#ifdef HAVE_LDAP_SET_OPTION
45 DEFAULT_PROTOCOL = 2,
46#endif
47 DEFAULT_PORT = 389 45 DEFAULT_PORT = 389
48}; 46};
49 47
50static int process_arguments (int, char **); 48typedef struct {
51static int validate_arguments (void); 49 int errorcode;
52static void print_help (void); 50 check_ldap_config config;
53void print_usage (void); 51} check_ldap_config_wrapper;
54 52static check_ldap_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
55static char ld_defattr[] = "(objectclass=*)"; 53static check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper /*config_wrapper*/);
56static char *ld_attr = ld_defattr;
57static char *ld_host = NULL;
58static char *ld_base = NULL;
59static char *ld_passwd = NULL;
60static char *ld_binddn = NULL;
61static int ld_port = -1;
62#ifdef HAVE_LDAP_SET_OPTION
63static int ld_protocol = DEFAULT_PROTOCOL;
64#endif
65#ifndef LDAP_OPT_SUCCESS
66# define LDAP_OPT_SUCCESS LDAP_SUCCESS
67#endif
68static double warn_time = UNDEFINED;
69static double crit_time = UNDEFINED;
70static thresholds *entries_thresholds = NULL;
71static struct timeval tv;
72static char* warn_entries = NULL;
73static char* crit_entries = NULL;
74static bool starttls = false;
75static bool ssl_on_connect = false;
76static bool verbose = false;
77
78/* for ldap tls */
79
80static char *SERVICE = "LDAP";
81
82int
83main (int argc, char *argv[])
84{
85
86 LDAP *ld;
87 LDAPMessage *result;
88
89 /* should be int result = STATE_UNKNOWN; */
90
91 int status = STATE_UNKNOWN;
92 long microsec;
93 double elapsed_time;
94
95 /* for ldap tls */
96 54
97 int tls; 55static void print_help(void);
98 int version=3; 56void print_usage(void);
99 57
100 int status_entries = STATE_OK; 58#ifndef LDAP_OPT_SUCCESS
101 int num_entries = 0; 59# define LDAP_OPT_SUCCESS LDAP_SUCCESS
60#endif
61static int verbose = 0;
102 62
103 setlocale (LC_ALL, ""); 63int main(int argc, char *argv[]) {
104 bindtextdomain (PACKAGE, LOCALEDIR); 64 setlocale(LC_ALL, "");
105 textdomain (PACKAGE); 65 bindtextdomain(PACKAGE, LOCALEDIR);
66 textdomain(PACKAGE);
106 67
107 if (strstr(argv[0],"check_ldaps")) { 68 if (strstr(argv[0], "check_ldaps")) {
108 xasprintf (&progname, "check_ldaps"); 69 xasprintf(&progname, "check_ldaps");
109 } 70 }
110 71
111 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
112 argv=np_extra_opts (&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
113 74
114 if (process_arguments (argc, argv) == ERROR) 75 check_ldap_config_wrapper tmp_config = process_arguments(argc, argv);
115 usage4 (_("Could not parse arguments")); 76 if (tmp_config.errorcode == ERROR) {
77 usage4(_("Could not parse arguments"));
78 }
116 79
117 if (strstr(argv[0],"check_ldaps") && ! starttls && ! ssl_on_connect) 80 const check_ldap_config config = tmp_config.config;
118 starttls = true;
119 81
120 /* initialize alarm signal handling */ 82 /* initialize alarm signal handling */
121 signal (SIGALRM, socket_timeout_alarm_handler); 83 signal(SIGALRM, socket_timeout_alarm_handler);
122 84
123 /* set socket timeout */ 85 /* set socket timeout */
124 alarm (socket_timeout); 86 alarm(socket_timeout);
125 87
126 /* get the start time */ 88 /* get the start time */
127 gettimeofday (&tv, NULL); 89 struct timeval start_time;
90 gettimeofday(&start_time, NULL);
128 91
92 LDAP *ldap_connection;
129 /* initialize ldap */ 93 /* initialize ldap */
130#ifdef HAVE_LDAP_INIT 94#ifdef HAVE_LDAP_INIT
131 if (!(ld = ldap_init (ld_host, ld_port))) { 95 if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) {
132 printf ("Could not connect to the server at port %i\n", ld_port); 96 printf("Could not connect to the server at port %i\n", config.ld_port);
133 return STATE_CRITICAL; 97 return STATE_CRITICAL;
134 } 98 }
135#else 99#else
136 if (!(ld = ldap_open (ld_host, ld_port))) { 100 if (!(ld = ldap_open(config.ld_host, config.ld_port))) {
137 if (verbose) 101 if (verbose) {
138 ldap_perror(ld, "ldap_open"); 102 ldap_perror(ldap_connection, "ldap_open");
139 printf (_("Could not connect to the server at port %i\n"), ld_port); 103 }
104 printf(_("Could not connect to the server at port %i\n"), config.ld_port);
140 return STATE_CRITICAL; 105 return STATE_CRITICAL;
141 } 106 }
142#endif /* HAVE_LDAP_INIT */ 107#endif /* HAVE_LDAP_INIT */
143 108
144#ifdef HAVE_LDAP_SET_OPTION 109#ifdef HAVE_LDAP_SET_OPTION
145 /* set ldap options */ 110 /* set ldap options */
146 if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &ld_protocol) != 111 if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) !=
147 LDAP_OPT_SUCCESS ) { 112 LDAP_OPT_SUCCESS) {
148 printf(_("Could not set protocol version %d\n"), ld_protocol); 113 printf(_("Could not set protocol version %d\n"), config.ld_protocol);
149 return STATE_CRITICAL; 114 return STATE_CRITICAL;
150 } 115 }
151#endif 116#endif
152 117
153 if (ld_port == LDAPS_PORT || ssl_on_connect) { 118 int version = 3;
154 xasprintf (&SERVICE, "LDAPS"); 119 int tls;
120 if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) {
155#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) 121#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
156 /* ldaps: set option tls */ 122 /* ldaps: set option tls */
157 tls = LDAP_OPT_X_TLS_HARD; 123 tls = LDAP_OPT_X_TLS_HARD;
158 124
159 if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) 125 if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) {
160 { 126 if (verbose) {
161 if (verbose) 127 ldap_perror(ldap_connection, "ldaps_option");
162 ldap_perror(ld, "ldaps_option"); 128 }
163 printf (_("Could not init TLS at port %i!\n"), ld_port); 129 printf(_("Could not init TLS at port %i!\n"), config.ld_port);
164 return STATE_CRITICAL; 130 return STATE_CRITICAL;
165 } 131 }
166#else 132#else
167 printf (_("TLS not supported by the libraries!\n")); 133 printf(_("TLS not supported by the libraries!\n"));
168 return STATE_CRITICAL; 134 return STATE_CRITICAL;
169#endif /* LDAP_OPT_X_TLS */ 135#endif /* LDAP_OPT_X_TLS */
170 } else if (starttls) { 136 } else if (config.starttls) {
171 xasprintf (&SERVICE, "LDAP-TLS");
172#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) 137#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S)
173 /* ldap with startTLS: set option version */ 138 /* ldap with startTLS: set option version */
174 if (ldap_get_option(ld,LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS ) 139 if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) ==
175 { 140 LDAP_OPT_SUCCESS) {
176 if (version < LDAP_VERSION3) 141 if (version < LDAP_VERSION3) {
177 {
178 version = LDAP_VERSION3; 142 version = LDAP_VERSION3;
179 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); 143 ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version);
180 } 144 }
181 } 145 }
182 /* call start_tls */ 146 /* call start_tls */
183 if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS) 147 if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) {
184 { 148 if (verbose) {
185 if (verbose) 149 ldap_perror(ldap_connection, "ldap_start_tls");
186 ldap_perror(ld, "ldap_start_tls"); 150 }
187 printf (_("Could not init startTLS at port %i!\n"), ld_port); 151 printf(_("Could not init startTLS at port %i!\n"), config.ld_port);
188 return STATE_CRITICAL; 152 return STATE_CRITICAL;
189 } 153 }
190#else 154#else
191 printf (_("startTLS not supported by the library, needs LDAPv3!\n")); 155 printf(_("startTLS not supported by the library, needs LDAPv3!\n"));
192 return STATE_CRITICAL; 156 return STATE_CRITICAL;
193#endif /* HAVE_LDAP_START_TLS_S */ 157#endif /* HAVE_LDAP_START_TLS_S */
194 } 158 }
195 159
196 /* bind to the ldap server */ 160 /* bind to the ldap server */
197 if (ldap_bind_s (ld, ld_binddn, ld_passwd, LDAP_AUTH_SIMPLE) != 161 if (ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE) !=
198 LDAP_SUCCESS) { 162 LDAP_SUCCESS) {
199 if (verbose) 163 if (verbose) {
200 ldap_perror(ld, "ldap_bind"); 164 ldap_perror(ldap_connection, "ldap_bind");
201 printf (_("Could not bind to the LDAP server\n")); 165 }
166 printf(_("Could not bind to the LDAP server\n"));
202 return STATE_CRITICAL; 167 return STATE_CRITICAL;
203 } 168 }
204 169
170 LDAPMessage *result;
171 int num_entries = 0;
205 /* do a search of all objectclasses in the base dn */ 172 /* do a search of all objectclasses in the base dn */
206 if (ldap_search_s (ld, ld_base, (crit_entries!=NULL || warn_entries!=NULL) ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE, ld_attr, NULL, 0, &result) 173 if (ldap_search_s(ldap_connection, config.ld_base,
207 != LDAP_SUCCESS) { 174 (config.crit_entries != NULL || config.warn_entries != NULL)
208 if (verbose) 175 ? LDAP_SCOPE_SUBTREE
209 ldap_perror(ld, "ldap_search"); 176 : LDAP_SCOPE_BASE,
210 printf (_("Could not search/find objectclasses in %s\n"), ld_base); 177 config.ld_attr, NULL, 0, &result) != LDAP_SUCCESS) {
178 if (verbose) {
179 ldap_perror(ldap_connection, "ldap_search");
180 }
181 printf(_("Could not search/find objectclasses in %s\n"), config.ld_base);
211 return STATE_CRITICAL; 182 return STATE_CRITICAL;
212 } else if (crit_entries!=NULL || warn_entries!=NULL) { 183 }
213 num_entries = ldap_count_entries(ld, result); 184
185 if (config.crit_entries != NULL || config.warn_entries != NULL) {
186 num_entries = ldap_count_entries(ldap_connection, result);
214 } 187 }
215 188
216 /* unbind from the ldap server */ 189 /* unbind from the ldap server */
217 ldap_unbind (ld); 190 ldap_unbind(ldap_connection);
218 191
219 /* reset the alarm handler */ 192 /* reset the alarm handler */
220 alarm (0); 193 alarm(0);
221 194
222 /* calculate the elapsed time and compare to thresholds */ 195 /* calculate the elapsed time and compare to thresholds */
223 196
224 microsec = deltime (tv); 197 long microsec = deltime(start_time);
225 elapsed_time = (double)microsec / 1.0e6; 198 double elapsed_time = (double)microsec / 1.0e6;
226 199 mp_state_enum status = STATE_UNKNOWN;
227 if (crit_time!=UNDEFINED && elapsed_time>crit_time) 200 if (config.crit_time_set && elapsed_time > config.crit_time) {
228 status = STATE_CRITICAL; 201 status = STATE_CRITICAL;
229 else if (warn_time!=UNDEFINED && elapsed_time>warn_time) 202 } else if (config.warn_time_set && elapsed_time > config.warn_time) {
230 status = STATE_WARNING; 203 status = STATE_WARNING;
231 else 204 } else {
232 status = STATE_OK; 205 status = STATE_OK;
206 }
233 207
234 if(entries_thresholds != NULL) { 208 if (config.entries_thresholds != NULL) {
235 if (verbose) { 209 if (verbose) {
236 printf ("entries found: %d\n", num_entries); 210 printf("entries found: %d\n", num_entries);
237 print_thresholds("entry thresholds", entries_thresholds); 211 print_thresholds("entry thresholds", config.entries_thresholds);
238 } 212 }
239 status_entries = get_status(num_entries, entries_thresholds); 213 mp_state_enum status_entries = get_status(num_entries, config.entries_thresholds);
240 if (status_entries == STATE_CRITICAL) { 214 if (status_entries == STATE_CRITICAL) {
241 status = STATE_CRITICAL; 215 status = STATE_CRITICAL;
242 } else if (status != STATE_CRITICAL) { 216 } else if (status != STATE_CRITICAL) {
@@ -245,273 +219,281 @@ main (int argc, char *argv[])
245 } 219 }
246 220
247 /* print out the result */ 221 /* print out the result */
248 if (crit_entries!=NULL || warn_entries!=NULL) { 222 if (config.crit_entries != NULL || config.warn_entries != NULL) {
249 printf (_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), 223 printf(_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), state_text(status),
250 state_text (status), 224 num_entries, elapsed_time,
251 num_entries, 225 fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time,
252 elapsed_time, 226 config.crit_time_set, config.crit_time, true, 0, false, 0),
253 fperfdata ("time", elapsed_time, "s", 227 sperfdata("entries", (double)num_entries, "", config.warn_entries,
254 (int)warn_time, warn_time, 228 config.crit_entries, true, 0.0, false, 0.0));
255 (int)crit_time, crit_time,
256 true, 0, false, 0),
257 sperfdata ("entries", (double)num_entries, "",
258 warn_entries,
259 crit_entries,
260 true, 0.0, false, 0.0));
261 } else { 229 } else {
262 printf (_("LDAP %s - %.3f seconds response time|%s\n"), 230 printf(_("LDAP %s - %.3f seconds response time|%s\n"), state_text(status), elapsed_time,
263 state_text (status), 231 fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time,
264 elapsed_time, 232 config.crit_time_set, config.crit_time, true, 0, false, 0));
265 fperfdata ("time", elapsed_time, "s",
266 (int)warn_time, warn_time,
267 (int)crit_time, crit_time,
268 true, 0, false, 0));
269 } 233 }
270 234
271 return status; 235 exit(status);
272} 236}
273 237
274/* process command-line arguments */ 238/* process command-line arguments */
275int 239check_ldap_config_wrapper process_arguments(int argc, char **argv) {
276process_arguments (int argc, char **argv)
277{
278 int c;
279
280 int option = 0;
281 /* initialize the long option struct */ 240 /* initialize the long option struct */
282 static struct option longopts[] = { 241 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
283 {"help", no_argument, 0, 'h'}, 242 {"version", no_argument, 0, 'V'},
284 {"version", no_argument, 0, 'V'}, 243 {"timeout", required_argument, 0, 't'},
285 {"timeout", required_argument, 0, 't'}, 244 {"hostname", required_argument, 0, 'H'},
286 {"hostname", required_argument, 0, 'H'}, 245 {"base", required_argument, 0, 'b'},
287 {"base", required_argument, 0, 'b'}, 246 {"attr", required_argument, 0, 'a'},
288 {"attr", required_argument, 0, 'a'}, 247 {"bind", required_argument, 0, 'D'},
289 {"bind", required_argument, 0, 'D'}, 248 {"pass", required_argument, 0, 'P'},
290 {"pass", required_argument, 0, 'P'},
291#ifdef HAVE_LDAP_SET_OPTION 249#ifdef HAVE_LDAP_SET_OPTION
292 {"ver2", no_argument, 0, '2'}, 250 {"ver2", no_argument, 0, '2'},
293 {"ver3", no_argument, 0, '3'}, 251 {"ver3", no_argument, 0, '3'},
294#endif 252#endif
295 {"starttls", no_argument, 0, 'T'}, 253 {"starttls", no_argument, 0, 'T'},
296 {"ssl", no_argument, 0, 'S'}, 254 {"ssl", no_argument, 0, 'S'},
297 {"use-ipv4", no_argument, 0, '4'}, 255 {"use-ipv4", no_argument, 0, '4'},
298 {"use-ipv6", no_argument, 0, '6'}, 256 {"use-ipv6", no_argument, 0, '6'},
299 {"port", required_argument, 0, 'p'}, 257 {"port", required_argument, 0, 'p'},
300 {"warn", required_argument, 0, 'w'}, 258 {"warn", required_argument, 0, 'w'},
301 {"crit", required_argument, 0, 'c'}, 259 {"crit", required_argument, 0, 'c'},
302 {"warn-entries", required_argument, 0, 'W'}, 260 {"warn-entries", required_argument, 0, 'W'},
303 {"crit-entries", required_argument, 0, 'C'}, 261 {"crit-entries", required_argument, 0, 'C'},
304 {"verbose", no_argument, 0, 'v'}, 262 {"verbose", no_argument, 0, 'v'},
305 {0, 0, 0, 0} 263 {0, 0, 0, 0}};
264
265 check_ldap_config_wrapper result = {
266 .errorcode = OK,
267 .config = check_ldap_config_init(),
306 }; 268 };
307 269
308 if (argc < 2) 270 if (argc < 2) {
309 return ERROR; 271 result.errorcode = ERROR;
272 return result;
273 }
310 274
311 for (c = 1; c < argc; c++) { 275 for (int index = 1; index < argc; index++) {
312 if (strcmp ("-to", argv[c]) == 0) 276 if (strcmp("-to", argv[index]) == 0) {
313 strcpy (argv[c], "-t"); 277 strcpy(argv[index], "-t");
278 }
314 } 279 }
315 280
281 int option = 0;
316 while (true) { 282 while (true) {
317 c = getopt_long (argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); 283 int option_index =
284 getopt_long(argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option);
318 285
319 if (c == -1 || c == EOF) 286 if (option_index == -1 || option_index == EOF) {
320 break; 287 break;
288 }
321 289
322 switch (c) { 290 switch (option_index) {
323 case 'h': /* help */ 291 case 'h': /* help */
324 print_help (); 292 print_help();
325 exit (STATE_UNKNOWN); 293 exit(STATE_UNKNOWN);
326 case 'V': /* version */ 294 case 'V': /* version */
327 print_revision (progname, NP_VERSION); 295 print_revision(progname, NP_VERSION);
328 exit (STATE_UNKNOWN); 296 exit(STATE_UNKNOWN);
329 case 't': /* timeout period */ 297 case 't': /* timeout period */
330 if (!is_intnonneg (optarg)) 298 if (!is_intnonneg(optarg)) {
331 usage2 (_("Timeout interval must be a positive integer"), optarg); 299 usage2(_("Timeout interval must be a positive integer"), optarg);
332 else 300 } else {
333 socket_timeout = atoi (optarg); 301 socket_timeout = atoi(optarg);
302 }
334 break; 303 break;
335 case 'H': 304 case 'H':
336 ld_host = optarg; 305 result.config.ld_host = optarg;
337 break; 306 break;
338 case 'b': 307 case 'b':
339 ld_base = optarg; 308 result.config.ld_base = optarg;
340 break; 309 break;
341 case 'p': 310 case 'p':
342 ld_port = atoi (optarg); 311 result.config.ld_port = atoi(optarg);
343 break; 312 break;
344 case 'a': 313 case 'a':
345 ld_attr = optarg; 314 result.config.ld_attr = optarg;
346 break; 315 break;
347 case 'D': 316 case 'D':
348 ld_binddn = optarg; 317 result.config.ld_binddn = optarg;
349 break; 318 break;
350 case 'P': 319 case 'P':
351 ld_passwd = optarg; 320 result.config.ld_passwd = optarg;
352 break; 321 break;
353 case 'w': 322 case 'w':
354 warn_time = strtod (optarg, NULL); 323 result.config.warn_time_set = true;
324 result.config.warn_time = strtod(optarg, NULL);
355 break; 325 break;
356 case 'c': 326 case 'c':
357 crit_time = strtod (optarg, NULL); 327 result.config.crit_time_set = true;
328 result.config.crit_time = strtod(optarg, NULL);
358 break; 329 break;
359 case 'W': 330 case 'W':
360 warn_entries = optarg; 331 result.config.warn_entries = optarg;
361 break; 332 break;
362 case 'C': 333 case 'C':
363 crit_entries = optarg; 334 result.config.crit_entries = optarg;
364 break; 335 break;
365#ifdef HAVE_LDAP_SET_OPTION 336#ifdef HAVE_LDAP_SET_OPTION
366 case '2': 337 case '2':
367 ld_protocol = 2; 338 result.config.ld_protocol = 2;
368 break; 339 break;
369 case '3': 340 case '3':
370 ld_protocol = 3; 341 result.config.ld_protocol = 3;
371 break; 342 break;
372#endif 343#endif // HAVE_LDAP_SET_OPTION
373 case '4': 344 case '4':
374 address_family = AF_INET; 345 address_family = AF_INET;
375 break; 346 break;
376 case 'v': 347 case 'v':
377 verbose = true; 348 verbose++;
378 break; 349 break;
379 case 'T': 350 case 'T':
380 if (! ssl_on_connect) 351 if (!result.config.ssl_on_connect) {
381 starttls = true; 352 result.config.starttls = true;
382 else 353 } else {
383 usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl"); 354 usage_va(_("%s cannot be combined with %s"), "-T/--starttls", "-S/--ssl");
355 }
384 break; 356 break;
385 case 'S': 357 case 'S':
386 if (! starttls) { 358 if (!result.config.starttls) {
387 ssl_on_connect = true; 359 result.config.ssl_on_connect = true;
388 if (ld_port == -1) 360 if (result.config.ld_port == -1) {
389 ld_port = LDAPS_PORT; 361 result.config.ld_port = LDAPS_PORT;
390 } else 362 }
363 } else {
391 usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls"); 364 usage_va(_("%s cannot be combined with %s"), "-S/--ssl", "-T/--starttls");
365 }
392 break; 366 break;
393 case '6': 367 case '6':
394#ifdef USE_IPV6 368#ifdef USE_IPV6
395 address_family = AF_INET6; 369 address_family = AF_INET6;
396#else 370#else
397 usage (_("IPv6 support not available\n")); 371 usage(_("IPv6 support not available\n"));
398#endif 372#endif
399 break; 373 break;
400 default: 374 default:
401 usage5 (); 375 usage5();
402 } 376 }
403 } 377 }
404 378
405 c = optind; 379 int index = optind;
406 if (ld_host == NULL && is_host(argv[c])) 380 if ((result.config.ld_host == NULL) && is_host(argv[index])) {
407 ld_host = strdup (argv[c++]); 381 result.config.ld_host = strdup(argv[index++]);
382 }
408 383
409 if (ld_base == NULL && argv[c]) 384 if ((result.config.ld_base == NULL) && argv[index]) {
410 ld_base = strdup (argv[c++]); 385 result.config.ld_base = strdup(argv[index++]);
386 }
411 387
412 if (ld_port == -1) 388 if (result.config.ld_port == -1) {
413 ld_port = DEFAULT_PORT; 389 result.config.ld_port = DEFAULT_PORT;
390 }
414 391
415 return validate_arguments (); 392 if (strstr(argv[0], "check_ldaps") && !result.config.starttls &&
393 !result.config.ssl_on_connect) {
394 result.config.starttls = true;
395 }
396
397 return validate_arguments(result);
416} 398}
417 399
400check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wrapper) {
401 if (config_wrapper.config.ld_host == NULL || strlen(config_wrapper.config.ld_host) == 0) {
402 usage4(_("Please specify the host name\n"));
403 }
418 404
419int 405 if (config_wrapper.config.ld_base == NULL) {
420validate_arguments () 406 usage4(_("Please specify the LDAP base\n"));
421{ 407 }
422 if (ld_host==NULL || strlen(ld_host)==0)
423 usage4 (_("Please specify the host name\n"));
424 408
425 if (ld_base==NULL) 409 if (config_wrapper.config.crit_entries != NULL || config_wrapper.config.warn_entries != NULL) {
426 usage4 (_("Please specify the LDAP base\n")); 410 set_thresholds(&config_wrapper.config.entries_thresholds,
411 config_wrapper.config.warn_entries, config_wrapper.config.crit_entries);
412 }
427 413
428 if (crit_entries!=NULL || warn_entries!=NULL) { 414 if (config_wrapper.config.ld_passwd == NULL) {
429 set_thresholds(&entries_thresholds, 415 config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD");
430 warn_entries, crit_entries);
431 } 416 }
432 if (ld_passwd==NULL)
433 ld_passwd = getenv("LDAP_PASSWORD");
434 417
435 return OK; 418 return config_wrapper;
436} 419}
437 420
438 421void print_help(void) {
439void
440print_help (void)
441{
442 char *myport; 422 char *myport;
443 xasprintf (&myport, "%d", DEFAULT_PORT); 423 xasprintf(&myport, "%d", DEFAULT_PORT);
444 424
445 print_revision (progname, NP_VERSION); 425 print_revision(progname, NP_VERSION);
446 426
447 printf ("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n"); 427 printf("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n");
448 printf (COPYRIGHT, copyright, email); 428 printf(COPYRIGHT, copyright, email);
449 429
450 printf ("\n\n"); 430 printf("\n\n");
451 431
452 print_usage (); 432 print_usage();
453 433
454 printf (UT_HELP_VRSN); 434 printf(UT_HELP_VRSN);
455 printf (UT_EXTRA_OPTS); 435 printf(UT_EXTRA_OPTS);
456 436
457 printf (UT_HOST_PORT, 'p', myport); 437 printf(UT_HOST_PORT, 'p', myport);
458 438
459 printf (UT_IPv46); 439 printf(UT_IPv46);
460 440
461 printf (" %s\n", "-a [--attr]"); 441 printf(" %s\n", "-a [--attr]");
462 printf (" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\"")); 442 printf(" %s\n", _("ldap attribute to search (default: \"(objectclass=*)\""));
463 printf (" %s\n", "-b [--base]"); 443 printf(" %s\n", "-b [--base]");
464 printf (" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at")); 444 printf(" %s\n", _("ldap base (eg. ou=my unit, o=my org, c=at"));
465 printf (" %s\n", "-D [--bind]"); 445 printf(" %s\n", "-D [--bind]");
466 printf (" %s\n", _("ldap bind DN (if required)")); 446 printf(" %s\n", _("ldap bind DN (if required)"));
467 printf (" %s\n", "-P [--pass]"); 447 printf(" %s\n", "-P [--pass]");
468 printf (" %s\n", _("ldap password (if required, or set the password through environment variable 'LDAP_PASSWORD')")); 448 printf(" %s\n", _("ldap password (if required, or set the password through environment "
469 printf (" %s\n", "-T [--starttls]"); 449 "variable 'LDAP_PASSWORD')"));
470 printf (" %s\n", _("use starttls mechanism introduced in protocol version 3")); 450 printf(" %s\n", "-T [--starttls]");
471 printf (" %s\n", "-S [--ssl]"); 451 printf(" %s\n", _("use starttls mechanism introduced in protocol version 3"));
472 printf (" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"), LDAPS_PORT); 452 printf(" %s\n", "-S [--ssl]");
453 printf(" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"),
454 LDAPS_PORT);
473 455
474#ifdef HAVE_LDAP_SET_OPTION 456#ifdef HAVE_LDAP_SET_OPTION
475 printf (" %s\n", "-2 [--ver2]"); 457 printf(" %s\n", "-2 [--ver2]");
476 printf (" %s\n", _("use ldap protocol version 2")); 458 printf(" %s\n", _("use ldap protocol version 2"));
477 printf (" %s\n", "-3 [--ver3]"); 459 printf(" %s\n", "-3 [--ver3]");
478 printf (" %s\n", _("use ldap protocol version 3")); 460 printf(" %s\n", _("use ldap protocol version 3"));
479 printf (" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL); 461 printf(" (%s %d)\n", _("default protocol version:"), DEFAULT_PROTOCOL);
480#endif 462#endif
481 463
482 printf (UT_WARN_CRIT); 464 printf(UT_WARN_CRIT);
483 465
484 printf (" %s\n", "-W [--warn-entries]"); 466 printf(" %s\n", "-W [--warn-entries]");
485 printf (" %s\n", _("Number of found entries to result in warning status")); 467 printf(" %s\n", _("Number of found entries to result in warning status"));
486 printf (" %s\n", "-C [--crit-entries]"); 468 printf(" %s\n", "-C [--crit-entries]");
487 printf (" %s\n", _("Number of found entries to result in critical status")); 469 printf(" %s\n", _("Number of found entries to result in critical status"));
488 470
489 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 471 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
490 472
491 printf (UT_VERBOSE); 473 printf(UT_VERBOSE);
492 474
493 printf ("\n"); 475 printf("\n");
494 printf ("%s\n", _("Notes:")); 476 printf("%s\n", _("Notes:"));
495 printf (" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be")); 477 printf(" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be"));
496 printf (_(" implied (using default port %i) unless --port=636 is specified. In that case\n"), DEFAULT_PORT); 478 printf(_(" implied (using default port %i) unless --port=636 is specified. In that case\n"),
497 printf (" %s\n", _("'SSL on connect' will be used no matter how the plugin was called.")); 479 DEFAULT_PORT);
498 printf (" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' or '--ssl' flags")); 480 printf(" %s\n", _("'SSL on connect' will be used no matter how the plugin was called."));
499 printf (" %s\n", _("to define the behaviour explicitly instead.")); 481 printf(" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' "
500 printf (" %s\n", _("The parameters --warn-entries and --crit-entries are optional.")); 482 "or '--ssl' flags"));
483 printf(" %s\n", _("to define the behaviour explicitly instead."));
484 printf(" %s\n", _("The parameters --warn-entries and --crit-entries are optional."));
501 485
502 printf (UT_SUPPORT); 486 printf(UT_SUPPORT);
503} 487}
504 488
505void 489void print_usage(void) {
506print_usage (void) 490 printf("%s\n", _("Usage:"));
507{ 491 printf(" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]", progname);
508 printf ("%s\n", _("Usage:")); 492 printf("\n [-P <password>] [-w <warn_time>] [-c <crit_time>] [-t timeout]%s\n",
509 printf (" %s -H <host> -b <base_dn> [-p <port>] [-a <attr>] [-D <binddn>]",progname);
510 printf ("\n [-P <password>] [-w <warn_time>] [-c <crit_time>] [-t timeout]%s\n",
511#ifdef HAVE_LDAP_SET_OPTION 493#ifdef HAVE_LDAP_SET_OPTION
512 "\n [-2|-3] [-4|-6]" 494 "\n [-2|-3] [-4|-6]"
513#else 495#else
514 "" 496 ""
515#endif 497#endif
516 ); 498 );
517} 499}
diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h
new file mode 100644
index 00000000..c8a40610
--- /dev/null
+++ b/plugins/check_ldap.d/config.h
@@ -0,0 +1,60 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7static char ld_defattr[] = "(objectclass=*)";
8
9enum {
10#ifdef HAVE_LDAP_SET_OPTION
11 DEFAULT_PROTOCOL = 2,
12#endif
13};
14
15typedef struct {
16 char *ld_host;
17 char *ld_base;
18 char *ld_passwd;
19 char *ld_binddn;
20 char *ld_attr;
21 int ld_port;
22 bool starttls;
23 bool ssl_on_connect;
24#ifdef HAVE_LDAP_SET_OPTION
25 int ld_protocol;
26#endif
27
28 char *warn_entries;
29 char *crit_entries;
30 thresholds *entries_thresholds;
31 bool warn_time_set;
32 double warn_time;
33 bool crit_time_set;
34 double crit_time;
35} check_ldap_config;
36
37check_ldap_config check_ldap_config_init() {
38 check_ldap_config tmp = {
39 .ld_host = NULL,
40 .ld_base = NULL,
41 .ld_passwd = NULL,
42 .ld_binddn = NULL,
43 .ld_attr = ld_defattr,
44 .ld_port = -1,
45 .starttls = false,
46 .ssl_on_connect = false,
47#ifdef HAVE_LDAP_SET_OPTION
48 .ld_protocol = DEFAULT_PROTOCOL,
49#endif
50
51 .warn_entries = NULL,
52 .crit_entries = NULL,
53 .entries_thresholds = NULL,
54 .warn_time_set = false,
55 .warn_time = 0,
56 .crit_time_set = false,
57 .crit_time = 0,
58 };
59 return tmp;
60}
diff --git a/plugins/check_load.c b/plugins/check_load.c
index 1431d130..644cd604 100644
--- a/plugins/check_load.c
+++ b/plugins/check_load.c
@@ -1,350 +1,430 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_load plugin 3 * Monitoring check_load plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2007 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2007 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_load plugin 10 * This file contains the check_load plugin
11* 11 *
12* This plugin tests the current system load average. 12 * This plugin tests the current system load average.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_load"; 31const char *progname = "check_load";
32const char *copyright = "1999-2022"; 32const char *copyright = "1999-2022";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "./common.h" 35#include "./common.h"
36#include <string.h>
36#include "./runcmd.h" 37#include "./runcmd.h"
37#include "./utils.h" 38#include "./utils.h"
38#include "./popen.h" 39#include "./popen.h"
40#include "../lib/states.h"
41#include "../lib/output.h"
42#include "../lib/perfdata.h"
43#include "../lib/thresholds.h"
44#include "check_load.d/config.h"
39 45
40#include <string.h> 46// getloadavg comes from gnulib
41 47#include "../gl/stdlib.h"
42#ifdef HAVE_SYS_LOADAVG_H
43#include <sys/loadavg.h>
44#endif
45 48
46/* needed for compilation under NetBSD, as suggested by Andy Doran */ 49/* needed for compilation under NetBSD, as suggested by Andy Doran */
47#ifndef LOADAVG_1MIN 50#ifndef LOADAVG_1MIN
48#define LOADAVG_1MIN 0 51# define LOADAVG_1MIN 0
49#define LOADAVG_5MIN 1 52# define LOADAVG_5MIN 1
50#define LOADAVG_15MIN 2 53# define LOADAVG_15MIN 2
51#endif /* !defined LOADAVG_1MIN */ 54#endif /* !defined LOADAVG_1MIN */
52 55
56typedef struct {
57 int errorcode;
58 check_load_config config;
59} check_load_config_wrapper;
60static check_load_config_wrapper process_arguments(int argc, char **argv);
61
62void print_help(void);
63void print_usage(void);
64typedef struct {
65 int errorcode;
66 char **top_processes;
67} top_processes_result;
68static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show);
69
70typedef struct {
71 mp_range load[3];
72} parsed_thresholds;
73static parsed_thresholds get_threshold(char *arg) {
74 size_t index;
75 char *str = arg;
76 char *tmp_pointer;
77 bool valid = false;
78
79 parsed_thresholds result = {
80 .load =
81 {
82 mp_range_init(),
83 mp_range_init(),
84 mp_range_init(),
85 },
86 };
53 87
54static int process_arguments (int argc, char **argv); 88 size_t arg_length = strlen(arg);
55static int validate_arguments (void); 89 for (index = 0; index < 3; index++) {
56void print_help (void); 90 double tmp = strtod(str, &tmp_pointer);
57void print_usage (void); 91 if (tmp_pointer == str) {
58static int print_top_consuming_processes(); 92 break;
59 93 }
60static int n_procs_to_show = 0;
61
62/* strictly for pretty-print usage in loops */
63static const int nums[3] = { 1, 5, 15 };
64
65/* provide some fairly sane defaults */
66double wload[3] = { 0.0, 0.0, 0.0 };
67double cload[3] = { 0.0, 0.0, 0.0 };
68#define la1 la[0]
69#define la5 la[1]
70#define la15 la[2]
71
72char *status_line;
73bool take_into_account_cpus = false;
74
75static void
76get_threshold(char *arg, double *th)
77{
78 size_t i, n;
79 int valid = 0;
80 char *str = arg, *p;
81 94
82 n = strlen(arg); 95 result.load[index] = mp_range_set_end(result.load[index], mp_create_pd_value(tmp));
83 for(i = 0; i < 3; i++) {
84 th[i] = strtod(str, &p);
85 if(p == str) break;
86 96
87 valid = 1; 97 valid = true;
88 str = p + 1; 98 str = tmp_pointer + 1;
89 if(n <= (size_t)(str - arg)) break; 99 if (arg_length <= (size_t)(str - arg)) {
100 break;
101 }
90 } 102 }
91 103
92 /* empty argument or non-floatish, so warn about it and die */ 104 /* empty argument or non-floatish, so warn about it and die */
93 if(!i && !valid) usage (_("Warning threshold must be float or float triplet!\n")); 105 if (!index && !valid) {
106 usage(_("Warning threshold must be float or float triplet!\n"));
107 }
94 108
95 if(i != 2) { 109 if (index != 2) {
96 /* one or more numbers were given, so fill array with last 110 /* one or more numbers were given, so fill array with last
97 * we got (most likely to NOT produce the least expected result) */ 111 * we got (most likely to NOT produce the least expected result) */
98 for(n = i; n < 3; n++) th[n] = th[i]; 112 for (size_t tmp_index = index; tmp_index < 3; tmp_index++) {
113 result.load[tmp_index] = result.load[index];
114 }
99 } 115 }
116 return result;
100} 117}
101 118
102 119int main(int argc, char **argv) {
103int 120 setlocale(LC_ALL, "");
104main (int argc, char **argv) 121 bindtextdomain(PACKAGE, LOCALEDIR);
105{ 122 textdomain(PACKAGE);
106 int result = -1;
107 int i;
108 long numcpus;
109
110 double la[3] = { 0.0, 0.0, 0.0 }; /* NetBSD complains about uninitialized arrays */
111#ifndef HAVE_GETLOADAVG
112 char input_buffer[MAX_INPUT_BUFFER];
113#endif
114
115 setlocale (LC_ALL, "");
116 bindtextdomain (PACKAGE, LOCALEDIR);
117 textdomain (PACKAGE);
118 setlocale(LC_NUMERIC, "POSIX"); 123 setlocale(LC_NUMERIC, "POSIX");
119 124
120 /* Parse extra opts if any */ 125 /* Parse extra opts if any */
121 argv = np_extra_opts (&argc, argv, progname); 126 argv = np_extra_opts(&argc, argv, progname);
122 127
123 if (process_arguments (argc, argv) == ERROR) 128 check_load_config_wrapper tmp_config = process_arguments(argc, argv);
124 usage4 (_("Could not parse arguments")); 129 if (tmp_config.errorcode == ERROR) {
125 130 usage4(_("Could not parse arguments"));
126#ifdef HAVE_GETLOADAVG
127 result = getloadavg (la, 3);
128 if (result != 3)
129 return STATE_UNKNOWN;
130#else
131 child_process = spopen (PATH_TO_UPTIME);
132 if (child_process == NULL) {
133 printf (_("Error opening %s\n"), PATH_TO_UPTIME);
134 return STATE_UNKNOWN;
135 }
136 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
137 if (child_stderr == NULL) {
138 printf (_("Could not open stderr for %s\n"), PATH_TO_UPTIME);
139 }
140 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
141 if(strstr(input_buffer, "load average:")) {
142 sscanf (input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15);
143 }
144 else if(strstr(input_buffer, "load averages:")) {
145 sscanf (input_buffer, "%*[^l]load averages: %lf, %lf, %lf", &la1, &la5, &la15);
146 }
147 else {
148 printf (_("could not parse load from uptime %s: %d\n"), PATH_TO_UPTIME, result);
149 return STATE_UNKNOWN;
150 }
151
152 result = spclose (child_process);
153 if (result) {
154 printf (_("Error code %d returned in %s\n"), result, PATH_TO_UPTIME);
155 return STATE_UNKNOWN;
156 }
157#endif
158
159 if ((la[0] < 0.0) || (la[1] < 0.0) || (la[2] < 0.0)) {
160#ifdef HAVE_GETLOADAVG
161 printf (_("Error in getloadavg()\n"));
162#else
163 printf (_("Error processing %s\n"), PATH_TO_UPTIME);
164#endif
165 return STATE_UNKNOWN;
166 } 131 }
167 132
168 /* we got this far, so assume OK until we've measured */ 133 const check_load_config config = tmp_config.config;
169 result = STATE_OK; 134
135 double load_values[3] = {0, 0, 0};
170 136
171 xasprintf(&status_line, _("load average: %.2f, %.2f, %.2f"), la1, la5, la15); 137 // this should be getloadavg from gnulib, should work everywhereâ„¢
172 xasprintf(&status_line, ("total %s"), status_line); 138 int error = getloadavg(load_values, 3);
139 if (error != 3) {
140 die(STATE_UNKNOWN, _("Failed to retrieve load values"));
141 }
173 142
143 mp_check overall = mp_check_init();
144 if (config.output_format_set) {
145 mp_set_format(config.output_format);
146 }
174 147
175 double scaled_la[3] = { 0.0, 0.0, 0.0 };
176 bool is_using_scaled_load_values = false; 148 bool is_using_scaled_load_values = false;
177 149 long numcpus;
178 if (take_into_account_cpus == true && (numcpus = GET_NUMBER_OF_CPUS()) > 0) { 150 if (config.take_into_account_cpus && ((numcpus = GET_NUMBER_OF_CPUS()) > 0)) {
179 is_using_scaled_load_values = true; 151 is_using_scaled_load_values = true;
180 152
181 scaled_la[0] = la[0] / numcpus; 153 double scaled_la[3] = {
182 scaled_la[1] = la[1] / numcpus; 154 load_values[0] / numcpus,
183 scaled_la[2] = la[2] / numcpus; 155 load_values[1] / numcpus,
156 load_values[2] / numcpus,
157 };
158
159 mp_subcheck scaled_load_sc = mp_subcheck_init();
160 scaled_load_sc = mp_set_subcheck_default_state(scaled_load_sc, STATE_OK);
161 scaled_load_sc.output = "Scaled Load (divided by number of CPUs";
162
163 mp_perfdata pd_scaled_load1 = perfdata_init();
164 pd_scaled_load1.label = "scaled_load1";
165 pd_scaled_load1 = mp_set_pd_value(pd_scaled_load1, scaled_la[0]);
166 pd_scaled_load1 = mp_pd_set_thresholds(pd_scaled_load1, config.th_load[0]);
167
168 mp_subcheck scaled_load_sc1 = mp_subcheck_init();
169 scaled_load_sc1 = mp_set_subcheck_state(scaled_load_sc1, mp_get_pd_status(pd_scaled_load1));
170 mp_add_perfdata_to_subcheck(&scaled_load_sc1, pd_scaled_load1);
171 xasprintf(&scaled_load_sc1.output, "1 Minute: %s",
172 pd_value_to_string(pd_scaled_load1.value));
173 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc1);
174
175 mp_perfdata pd_scaled_load5 = perfdata_init();
176 pd_scaled_load5.label = "scaled_load5";
177 pd_scaled_load5 = mp_set_pd_value(pd_scaled_load5, scaled_la[1]);
178 pd_scaled_load5 = mp_pd_set_thresholds(pd_scaled_load5, config.th_load[1]);
179
180 mp_subcheck scaled_load_sc5 = mp_subcheck_init();
181 scaled_load_sc5 = mp_set_subcheck_state(scaled_load_sc5, mp_get_pd_status(pd_scaled_load5));
182 mp_add_perfdata_to_subcheck(&scaled_load_sc5, pd_scaled_load5);
183 xasprintf(&scaled_load_sc5.output, "5 Minutes: %s",
184 pd_value_to_string(pd_scaled_load5.value));
185 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc5);
186
187 mp_perfdata pd_scaled_load15 = perfdata_init();
188 pd_scaled_load15.label = "scaled_load15";
189 pd_scaled_load15 = mp_set_pd_value(pd_scaled_load15, scaled_la[2]);
190 pd_scaled_load15 = mp_pd_set_thresholds(pd_scaled_load15, config.th_load[2]);
191
192 mp_subcheck scaled_load_sc15 = mp_subcheck_init();
193 scaled_load_sc15 =
194 mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15));
195 mp_add_perfdata_to_subcheck(&scaled_load_sc15, pd_scaled_load15);
196 xasprintf(&scaled_load_sc15.output, "15 Minutes: %s",
197 pd_value_to_string(pd_scaled_load15.value));
198 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc15);
199
200 mp_add_subcheck_to_check(&overall, scaled_load_sc);
201 }
202
203 mp_subcheck load_sc = mp_subcheck_init();
204 load_sc = mp_set_subcheck_default_state(load_sc, STATE_OK);
205 load_sc.output = "Total Load";
184 206
185 char *tmp = NULL; 207 mp_perfdata pd_load1 = perfdata_init();
186 xasprintf(&tmp, _("load average: %.2f, %.2f, %.2f"), scaled_la[0], scaled_la[1], scaled_la[2]); 208 pd_load1.label = "load1";
187 xasprintf(&status_line, "scaled %s - %s", tmp, status_line); 209 pd_load1 = mp_set_pd_value(pd_load1, load_values[0]);
210 if (!is_using_scaled_load_values) {
211 pd_load1 = mp_pd_set_thresholds(pd_load1, config.th_load[0]);
188 } 212 }
189 213
190 for(i = 0; i < 3; i++) { 214 mp_subcheck load_sc1 = mp_subcheck_init();
191 if (is_using_scaled_load_values) { 215 load_sc1 = mp_set_subcheck_state(load_sc1, mp_get_pd_status(pd_load1));
192 if(scaled_la[i] > cload[i]) { 216 mp_add_perfdata_to_subcheck(&load_sc1, pd_load1);
193 result = STATE_CRITICAL; 217 xasprintf(&load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_load1.value));
194 break; 218 mp_add_subcheck_to_subcheck(&load_sc, load_sc1);
195 } 219
196 else if(scaled_la[i] > wload[i]) result = STATE_WARNING; 220 mp_perfdata pd_load5 = perfdata_init();
197 } else { 221 pd_load5.label = "load5";
198 if(la[i] > cload[i]) { 222 pd_load5 = mp_set_pd_value(pd_load5, load_values[1]);
199 result = STATE_CRITICAL; 223 if (!is_using_scaled_load_values) {
200 break; 224 pd_load5 = mp_pd_set_thresholds(pd_load5, config.th_load[1]);
201 }
202 else if(la[i] > wload[i]) result = STATE_WARNING;
203 }
204 } 225 }
205 226
206 printf("LOAD %s - %s|", state_text(result), status_line); 227 mp_subcheck load_sc5 = mp_subcheck_init();
207 for(i = 0; i < 3; i++) { 228 load_sc5 = mp_set_subcheck_state(load_sc5, mp_get_pd_status(pd_load5));
208 if (is_using_scaled_load_values) { 229 mp_add_perfdata_to_subcheck(&load_sc5, pd_load5);
209 printf("load%d=%.3f;;;0; ", nums[i], la[i]); 230 xasprintf(&load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_load5.value));
210 printf("scaled_load%d=%.3f;%.3f;%.3f;0; ", nums[i], scaled_la[i], wload[i], cload[i]); 231 mp_add_subcheck_to_subcheck(&load_sc, load_sc5);
211 } else { 232
212 printf("load%d=%.3f;%.3f;%.3f;0; ", nums[i], la[i], wload[i], cload[i]); 233 mp_perfdata pd_load15 = perfdata_init();
213 } 234 pd_load15.label = "load15";
235 pd_load15 = mp_set_pd_value(pd_load15, load_values[2]);
236 if (!is_using_scaled_load_values) {
237 pd_load15 = mp_pd_set_thresholds(pd_load15, config.th_load[2]);
214 } 238 }
215 239
216 putchar('\n'); 240 mp_subcheck load_sc15 = mp_subcheck_init();
217 if (n_procs_to_show > 0) { 241 load_sc15 = mp_set_subcheck_state(load_sc15, mp_get_pd_status(pd_load15));
218 print_top_consuming_processes(); 242 mp_add_perfdata_to_subcheck(&load_sc15, pd_load15);
243 xasprintf(&load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_load15.value));
244 mp_add_subcheck_to_subcheck(&load_sc, load_sc15);
245
246 mp_add_subcheck_to_check(&overall, load_sc);
247
248 if (config.n_procs_to_show > 0) {
249 mp_subcheck top_proc_sc = mp_subcheck_init();
250 top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK);
251 top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show);
252 xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes",
253 config.n_procs_to_show);
254
255 if (top_proc.errorcode == OK) {
256 for (unsigned long i = 0; i < config.n_procs_to_show; i++) {
257 xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output,
258 top_proc.top_processes[i]);
259 }
260 }
261
262 mp_add_subcheck_to_check(&overall, top_proc_sc);
219 } 263 }
220 return result;
221}
222 264
265 mp_exit(overall);
266}
223 267
224/* process command-line arguments */ 268/* process command-line arguments */
225static int 269static check_load_config_wrapper process_arguments(int argc, char **argv) {
226process_arguments (int argc, char **argv) 270
227{ 271 enum {
228 int c = 0; 272 output_format_index = CHAR_MAX + 1,
229
230 int option = 0;
231 static struct option longopts[] = {
232 {"warning", required_argument, 0, 'w'},
233 {"critical", required_argument, 0, 'c'},
234 {"percpu", no_argument, 0, 'r'},
235 {"version", no_argument, 0, 'V'},
236 {"help", no_argument, 0, 'h'},
237 {"procs-to-show", required_argument, 0, 'n'},
238 {0, 0, 0, 0}
239 }; 273 };
240 274
241 if (argc < 2) 275 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
242 return ERROR; 276 {"critical", required_argument, 0, 'c'},
277 {"percpu", no_argument, 0, 'r'},
278 {"version", no_argument, 0, 'V'},
279 {"help", no_argument, 0, 'h'},
280 {"procs-to-show", required_argument, 0, 'n'},
281 {"output-format", required_argument, 0, output_format_index},
282 {0, 0, 0, 0}};
283
284 check_load_config_wrapper result = {
285 .errorcode = OK,
286 .config = check_load_config_init(),
287 };
243 288
244 while (1) { 289 if (argc < 2) {
245 c = getopt_long (argc, argv, "Vhrc:w:n:", longopts, &option); 290 result.errorcode = ERROR;
291 return result;
292 }
246 293
247 if (c == -1 || c == EOF) 294 while (true) {
248 break; 295 int option = 0;
296 int option_index = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option);
249 297
250 switch (c) { 298 if (option_index == -1 || option_index == EOF) {
251 case 'w': /* warning time threshold */
252 get_threshold(optarg, wload);
253 break; 299 break;
254 case 'c': /* critical time threshold */ 300 }
255 get_threshold(optarg, cload); 301
302 switch (option_index) {
303 case output_format_index: {
304 parsed_output_format parser = mp_parse_output_format(optarg);
305 if (!parser.parsing_success) {
306 printf("Invalid output format: %s\n", optarg);
307 exit(STATE_UNKNOWN);
308 }
309
310 result.config.output_format_set = true;
311 result.config.output_format = parser.output_format;
256 break; 312 break;
313 }
314 case 'w': /* warning time threshold */ {
315 parsed_thresholds warning_range = get_threshold(optarg);
316 result.config.th_load[0].warning = warning_range.load[0];
317 result.config.th_load[0].warning_is_set = true;
318
319 result.config.th_load[1].warning = warning_range.load[1];
320 result.config.th_load[1].warning_is_set = true;
321
322 result.config.th_load[2].warning = warning_range.load[2];
323 result.config.th_load[2].warning_is_set = true;
324 } break;
325 case 'c': /* critical time threshold */ {
326 parsed_thresholds critical_range = get_threshold(optarg);
327 result.config.th_load[0].critical = critical_range.load[0];
328 result.config.th_load[0].critical_is_set = true;
329
330 result.config.th_load[1].critical = critical_range.load[1];
331 result.config.th_load[1].critical_is_set = true;
332
333 result.config.th_load[2].critical = critical_range.load[2];
334 result.config.th_load[2].critical_is_set = true;
335 } break;
257 case 'r': /* Divide load average by number of CPUs */ 336 case 'r': /* Divide load average by number of CPUs */
258 take_into_account_cpus = true; 337 result.config.take_into_account_cpus = true;
259 break; 338 break;
260 case 'V': /* version */ 339 case 'V': /* version */
261 print_revision (progname, NP_VERSION); 340 print_revision(progname, NP_VERSION);
262 exit (STATE_UNKNOWN); 341 exit(STATE_UNKNOWN);
263 case 'h': /* help */ 342 case 'h': /* help */
264 print_help (); 343 print_help();
265 exit (STATE_UNKNOWN); 344 exit(STATE_UNKNOWN);
266 case 'n': 345 case 'n':
267 n_procs_to_show = atoi(optarg); 346 result.config.n_procs_to_show = (unsigned long)atol(optarg);
268 break; 347 break;
269 case '?': /* help */ 348 case '?': /* help */
270 usage5 (); 349 usage5();
271 } 350 }
272 } 351 }
273 352
274 c = optind; 353 int index = optind;
275 if (c == argc) 354 if (index == argc) {
276 return validate_arguments (); 355 return result;
356 }
277 357
278 /* handle the case if both arguments are missing, 358 /* handle the case if both arguments are missing,
279 * but not if only one is given without -c or -w flag */ 359 * but not if only one is given without -c or -w flag */
280 if(c - argc == 2) { 360 if (index - argc == 2) {
281 get_threshold(argv[c++], wload); 361 parsed_thresholds warning_range = get_threshold(argv[index++]);
282 get_threshold(argv[c++], cload); 362 result.config.th_load[0].warning = warning_range.load[0];
283 } 363 result.config.th_load[0].warning_is_set = true;
284 else if(c - argc == 1) { 364
285 get_threshold(argv[c++], cload); 365 result.config.th_load[1].warning = warning_range.load[1];
286 } 366 result.config.th_load[1].warning_is_set = true;
287 367
288 return validate_arguments (); 368 result.config.th_load[2].warning = warning_range.load[2];
289} 369 result.config.th_load[2].warning_is_set = true;
290 370 parsed_thresholds critical_range = get_threshold(argv[index++]);
291 371 result.config.th_load[0].critical = critical_range.load[0];
292static int 372 result.config.th_load[0].critical_is_set = true;
293validate_arguments (void) 373
294{ 374 result.config.th_load[1].critical = critical_range.load[1];
295 int i = 0; 375 result.config.th_load[1].critical_is_set = true;
296 376
297 /* match cload first, as it will give the most friendly error message 377 result.config.th_load[2].critical = critical_range.load[2];
298 * if user hasn't given the -c switch properly */ 378 result.config.th_load[2].critical_is_set = true;
299 for(i = 0; i < 3; i++) { 379 } else if (index - argc == 1) {
300 if(cload[i] < 0) 380 parsed_thresholds critical_range = get_threshold(argv[index++]);
301 die (STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]); 381 result.config.th_load[0].critical = critical_range.load[0];
302 if(wload[i] < 0) 382 result.config.th_load[0].critical_is_set = true;
303 die (STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]); 383
304 if(wload[i] > cload[i]) 384 result.config.th_load[1].critical = critical_range.load[1];
305 die (STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]); 385 result.config.th_load[1].critical_is_set = true;
386
387 result.config.th_load[2].critical = critical_range.load[2];
388 result.config.th_load[2].critical_is_set = true;
306 } 389 }
307 390
308 return OK; 391 return result;
309} 392}
310 393
394void print_help(void) {
395 print_revision(progname, NP_VERSION);
311 396
312void 397 printf("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n");
313print_help (void) 398 printf(COPYRIGHT, copyright, email);
314{
315 print_revision (progname, NP_VERSION);
316 399
317 printf ("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n"); 400 printf(_("This plugin tests the current system load average."));
318 printf (COPYRIGHT, copyright, email);
319 401
320 printf (_("This plugin tests the current system load average.")); 402 printf("\n\n");
321 403
322 printf ("\n\n"); 404 print_usage();
323 405
324 print_usage (); 406 printf(UT_HELP_VRSN);
407 printf(UT_EXTRA_OPTS);
325 408
326 printf (UT_HELP_VRSN); 409 printf(" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15");
327 printf (UT_EXTRA_OPTS); 410 printf(" %s\n", _("Exit with WARNING status if load average exceeds WLOADn"));
411 printf(" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15");
412 printf(" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn"));
413 printf(" %s\n", _("the load average format is the same used by \"uptime\" and \"w\""));
414 printf(" %s\n", "-r, --percpu");
415 printf(" %s\n", _("Divide the load averages by the number of CPUs (when possible)"));
416 printf(" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS");
417 printf(" %s\n", _("Number of processes to show when printing the top consuming processes."));
418 printf(" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0"));
328 419
329 printf (" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); 420 printf(UT_OUTPUT_FORMAT);
330 printf (" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); 421 printf(UT_SUPPORT);
331 printf (" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15");
332 printf (" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn"));
333 printf (" %s\n", _("the load average format is the same used by \"uptime\" and \"w\""));
334 printf (" %s\n", "-r, --percpu");
335 printf (" %s\n", _("Divide the load averages by the number of CPUs (when possible)"));
336 printf (" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS");
337 printf (" %s\n", _("Number of processes to show when printing the top consuming processes."));
338 printf (" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0"));
339
340 printf (UT_SUPPORT);
341} 422}
342 423
343void 424void print_usage(void) {
344print_usage (void) 425 printf("%s\n", _("Usage:"));
345{ 426 printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n",
346 printf ("%s\n", _("Usage:")); 427 progname);
347 printf ("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname);
348} 428}
349 429
350#ifdef PS_USES_PROCPCPU 430#ifdef PS_USES_PROCPCPU
@@ -356,36 +436,52 @@ int cmpstringp(const void *p1, const void *p2) {
356 int procrss = 0; 436 int procrss = 0;
357 float procpcpu = 0; 437 float procpcpu = 0;
358 char procstat[8]; 438 char procstat[8];
359#ifdef PS_USES_PROCETIME 439# ifdef PS_USES_PROCETIME
360 char procetime[MAX_INPUT_BUFFER]; 440 char procetime[MAX_INPUT_BUFFER];
361#endif /* PS_USES_PROCETIME */ 441# endif /* PS_USES_PROCETIME */
362 char procprog[MAX_INPUT_BUFFER]; 442 char procprog[MAX_INPUT_BUFFER];
363 int pos; 443 int pos;
364 sscanf (* (char * const *) p1, PS_FORMAT, PS_VARLIST); 444 sscanf(*(char *const *)p1, PS_FORMAT, PS_VARLIST);
365 float procpcpu1 = procpcpu; 445 float procpcpu1 = procpcpu;
366 sscanf (* (char * const *) p2, PS_FORMAT, PS_VARLIST); 446 sscanf(*(char *const *)p2, PS_FORMAT, PS_VARLIST);
367 return procpcpu1 < procpcpu; 447 return procpcpu1 < procpcpu;
368} 448}
369#endif /* PS_USES_PROCPCPU */ 449#endif /* PS_USES_PROCPCPU */
370 450
371static int print_top_consuming_processes() { 451static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show) {
372 int i = 0; 452 top_processes_result result = {
373 struct output chld_out, chld_err; 453 .errorcode = OK,
374 if(np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0){ 454 };
455 output chld_out;
456 output chld_err;
457 if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) {
375 fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); 458 fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND);
376 return STATE_UNKNOWN; 459 result.errorcode = ERROR;
460 return result;
377 } 461 }
462
378 if (chld_out.lines < 2) { 463 if (chld_out.lines < 2) {
379 fprintf(stderr, _("some error occurred getting procs list.\n")); 464 fprintf(stderr, _("some error occurred getting procs list.\n"));
380 return STATE_UNKNOWN; 465 result.errorcode = ERROR;
466 return result;
381 } 467 }
468
382#ifdef PS_USES_PROCPCPU 469#ifdef PS_USES_PROCPCPU
383 qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char*), cmpstringp); 470 qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp);
384#endif /* PS_USES_PROCPCPU */ 471#endif /* PS_USES_PROCPCPU */
385 int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) 472 unsigned long lines_to_show =
386 ? (int)chld_out.lines : n_procs_to_show + 1; 473 chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1;
387 for (i = 0; i < lines_to_show; i += 1) { 474
388 printf("%s\n", chld_out.line[i]); 475 result.top_processes = calloc(lines_to_show, sizeof(char *));
476 if (result.top_processes == NULL) {
477 // Failed allocation
478 result.errorcode = ERROR;
479 return result;
389 } 480 }
390 return OK; 481
482 for (unsigned long i = 0; i < lines_to_show; i += 1) {
483 xasprintf(&result.top_processes[i], "%s", chld_out.line[i]);
484 }
485
486 return result;
391} 487}
diff --git a/plugins/check_load.d/config.h b/plugins/check_load.d/config.h
new file mode 100644
index 00000000..fd735455
--- /dev/null
+++ b/plugins/check_load.d/config.h
@@ -0,0 +1,30 @@
1#pragma once
2
3#include "output.h"
4#include "thresholds.h"
5typedef struct {
6 mp_thresholds th_load[3];
7
8 bool take_into_account_cpus;
9 unsigned long n_procs_to_show;
10
11 mp_output_format output_format;
12 bool output_format_set;
13} check_load_config;
14
15check_load_config check_load_config_init() {
16 check_load_config tmp = {
17 .th_load =
18 {
19 mp_thresholds_init(),
20 mp_thresholds_init(),
21 mp_thresholds_init(),
22 },
23
24 .take_into_account_cpus = false,
25 .n_procs_to_show = 0,
26
27 .output_format_set = false,
28 };
29 return tmp;
30}
diff --git a/plugins/check_mrtg.c b/plugins/check_mrtg.c
index 632e66fb..4a17049a 100644
--- a/plugins/check_mrtg.c
+++ b/plugins/check_mrtg.c
@@ -35,21 +35,18 @@ const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
37#include "utils.h" 37#include "utils.h"
38#include "check_mrtg.d/config.h"
39
40typedef struct {
41 int errorcode;
42 check_mrtg_config config;
43} check_mrtg_config_wrapper;
44static check_mrtg_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
45static check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper /*config_wrapper*/);
38 46
39static int process_arguments(int /*argc*/, char ** /*argv*/);
40static int validate_arguments(void);
41static void print_help(void); 47static void print_help(void);
42void print_usage(void); 48void print_usage(void);
43 49
44static char *log_file = NULL;
45static int expire_minutes = 0;
46static bool use_average = true;
47static int variable_number = -1;
48static unsigned long value_warning_threshold = 0L;
49static unsigned long value_critical_threshold = 0L;
50static char *label;
51static char *units;
52
53int main(int argc, char **argv) { 50int main(int argc, char **argv) {
54 setlocale(LC_ALL, ""); 51 setlocale(LC_ALL, "");
55 bindtextdomain(PACKAGE, LOCALEDIR); 52 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -58,32 +55,37 @@ int main(int argc, char **argv) {
58 /* Parse extra opts if any */ 55 /* Parse extra opts if any */
59 argv = np_extra_opts(&argc, argv, progname); 56 argv = np_extra_opts(&argc, argv, progname);
60 57
61 if (process_arguments(argc, argv) == ERROR) 58 check_mrtg_config_wrapper tmp_config = process_arguments(argc, argv);
59 if (tmp_config.errorcode == ERROR) {
62 usage4(_("Could not parse arguments\n")); 60 usage4(_("Could not parse arguments\n"));
61 }
62
63 const check_mrtg_config config = tmp_config.config;
63 64
64 /* open the MRTG log file for reading */ 65 /* open the MRTG log file for reading */
65 FILE *mtrg_log_file = fopen(log_file, "r"); 66 FILE *mtrg_log_file = fopen(config.log_file, "r");
66 if (mtrg_log_file == NULL) { 67 if (mtrg_log_file == NULL) {
67 printf(_("Unable to open MRTG log file\n")); 68 printf(_("Unable to open MRTG log file\n"));
68 return STATE_UNKNOWN; 69 return STATE_UNKNOWN;
69 } 70 }
70 71
71 time_t timestamp = 0L; 72 time_t timestamp = 0;
72 unsigned long average_value_rate = 0L; 73 unsigned long average_value_rate = 0;
73 unsigned long maximum_value_rate = 0L; 74 unsigned long maximum_value_rate = 0;
74 char input_buffer[MAX_INPUT_BUFFER]; 75 char input_buffer[MAX_INPUT_BUFFER];
75 int line = 0; 76 int line = 0;
76 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) { 77 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, mtrg_log_file)) {
77
78 line++; 78 line++;
79 79
80 /* skip the first line of the log file */ 80 /* skip the first line of the log file */
81 if (line == 1) 81 if (line == 1) {
82 continue; 82 continue;
83 }
83 84
84 /* break out of read loop if we've passed the number of entries we want to read */ 85 /* break out of read loop if we've passed the number of entries we want to read */
85 if (line > 2) 86 if (line > 2) {
86 break; 87 break;
88 }
87 89
88 /* grab the timestamp */ 90 /* grab the timestamp */
89 char *temp_buffer = strtok(input_buffer, " "); 91 char *temp_buffer = strtok(input_buffer, " ");
@@ -91,23 +93,27 @@ int main(int argc, char **argv) {
91 93
92 /* grab the average value 1 rate */ 94 /* grab the average value 1 rate */
93 temp_buffer = strtok(NULL, " "); 95 temp_buffer = strtok(NULL, " ");
94 if (variable_number == 1) 96 if (config.variable_number == 1) {
95 average_value_rate = strtoul(temp_buffer, NULL, 10); 97 average_value_rate = strtoul(temp_buffer, NULL, 10);
98 }
96 99
97 /* grab the average value 2 rate */ 100 /* grab the average value 2 rate */
98 temp_buffer = strtok(NULL, " "); 101 temp_buffer = strtok(NULL, " ");
99 if (variable_number == 2) 102 if (config.variable_number == 2) {
100 average_value_rate = strtoul(temp_buffer, NULL, 10); 103 average_value_rate = strtoul(temp_buffer, NULL, 10);
104 }
101 105
102 /* grab the maximum value 1 rate */ 106 /* grab the maximum value 1 rate */
103 temp_buffer = strtok(NULL, " "); 107 temp_buffer = strtok(NULL, " ");
104 if (variable_number == 1) 108 if (config.variable_number == 1) {
105 maximum_value_rate = strtoul(temp_buffer, NULL, 10); 109 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
110 }
106 111
107 /* grab the maximum value 2 rate */ 112 /* grab the maximum value 2 rate */
108 temp_buffer = strtok(NULL, " "); 113 temp_buffer = strtok(NULL, " ");
109 if (variable_number == 2) 114 if (config.variable_number == 2) {
110 maximum_value_rate = strtoul(temp_buffer, NULL, 10); 115 maximum_value_rate = strtoul(temp_buffer, NULL, 10);
116 }
111 } 117 }
112 118
113 /* close the log file */ 119 /* close the log file */
@@ -122,49 +128,69 @@ int main(int argc, char **argv) {
122 /* make sure the MRTG data isn't too old */ 128 /* make sure the MRTG data isn't too old */
123 time_t current_time; 129 time_t current_time;
124 time(&current_time); 130 time(&current_time);
125 if (expire_minutes > 0 && (current_time - timestamp) > (expire_minutes * 60)) { 131 if (config.expire_minutes > 0 && (current_time - timestamp) > (config.expire_minutes * 60)) {
126 printf(_("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); 132 printf(_("MRTG data has expired (%d minutes old)\n"),
133 (int)((current_time - timestamp) / 60));
127 return STATE_WARNING; 134 return STATE_WARNING;
128 } 135 }
129 136
130 unsigned long rate = 0L; 137 unsigned long rate = 0L;
131 /* else check the incoming/outgoing rates */ 138 /* else check the incoming/outgoing rates */
132 if (use_average) 139 if (config.use_average) {
133 rate = average_value_rate; 140 rate = average_value_rate;
134 else 141 } else {
135 rate = maximum_value_rate; 142 rate = maximum_value_rate;
143 }
136 144
137 int result = STATE_OK; 145 int result = STATE_OK;
138 if (rate > value_critical_threshold) 146 if (config.value_critical_threshold_set && rate > config.value_critical_threshold) {
139 result = STATE_CRITICAL; 147 result = STATE_CRITICAL;
140 else if (rate > value_warning_threshold) 148 } else if (config.value_warning_threshold_set && rate > config.value_warning_threshold) {
141 result = STATE_WARNING; 149 result = STATE_WARNING;
150 }
142 151
143 printf("%s. %s = %lu %s|%s\n", (use_average) ? _("Avg") : _("Max"), label, rate, units, 152 printf("%s. %s = %lu %s|%s\n", (config.use_average) ? _("Avg") : _("Max"), config.label, rate,
144 perfdata(label, (long)rate, units, (int)value_warning_threshold, (long)value_warning_threshold, (int)value_critical_threshold, 153 config.units,
145 (long)value_critical_threshold, 0, 0, 0, 0)); 154 perfdata(config.label, (long)rate, config.units, config.value_warning_threshold_set,
155 (long)config.value_warning_threshold, config.value_critical_threshold_set,
156 (long)config.value_critical_threshold, 0, 0, 0, 0));
146 157
147 return result; 158 return result;
148} 159}
149 160
150/* process command-line arguments */ 161/* process command-line arguments */
151int process_arguments(int argc, char **argv) { 162check_mrtg_config_wrapper process_arguments(int argc, char **argv) {
152 static struct option longopts[] = { 163 static struct option longopts[] = {{"logfile", required_argument, 0, 'F'},
153 {"logfile", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, {"aggregation", required_argument, 0, 'a'}, 164 {"expires", required_argument, 0, 'e'},
154 {"variable", required_argument, 0, 'v'}, {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, 165 {"aggregation", required_argument, 0, 'a'},
155 {"label", required_argument, 0, 'l'}, {"units", required_argument, 0, 'u'}, {"variable", required_argument, 0, 'v'}, 166 {"variable", required_argument, 0, 'v'},
156 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 167 {"critical", required_argument, 0, 'c'},
157 168 {"warning", required_argument, 0, 'w'},
158 if (argc < 2) 169 {"label", required_argument, 0, 'l'},
159 return ERROR; 170 {"units", required_argument, 0, 'u'},
171 {"variable", required_argument, 0, 'v'},
172 {"version", no_argument, 0, 'V'},
173 {"help", no_argument, 0, 'h'},
174 {0, 0, 0, 0}};
175
176 check_mrtg_config_wrapper result = {
177 .errorcode = OK,
178 .config = check_mrtg_config_init(),
179 };
180
181 if (argc < 2) {
182 result.errorcode = ERROR;
183 return result;
184 }
160 185
161 for (int i = 1; i < argc; i++) { 186 for (int i = 1; i < argc; i++) {
162 if (strcmp("-to", argv[i]) == 0) 187 if (strcmp("-to", argv[i]) == 0) {
163 strcpy(argv[i], "-t"); 188 strcpy(argv[i], "-t");
164 else if (strcmp("-wt", argv[i]) == 0) 189 } else if (strcmp("-wt", argv[i]) == 0) {
165 strcpy(argv[i], "-w"); 190 strcpy(argv[i], "-w");
166 else if (strcmp("-ct", argv[i]) == 0) 191 } else if (strcmp("-ct", argv[i]) == 0) {
167 strcpy(argv[i], "-c"); 192 strcpy(argv[i], "-c");
193 }
168 } 194 }
169 195
170 int option_char; 196 int option_char;
@@ -172,38 +198,39 @@ int process_arguments(int argc, char **argv) {
172 while (1) { 198 while (1) {
173 option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option); 199 option_char = getopt_long(argc, argv, "hVF:e:a:v:c:w:l:u:", longopts, &option);
174 200
175 if (option_char == -1 || option_char == EOF) 201 if (option_char == -1 || option_char == EOF) {
176 break; 202 break;
203 }
177 204
178 switch (option_char) { 205 switch (option_char) {
179 case 'F': /* input file */ 206 case 'F': /* input file */
180 log_file = optarg; 207 result.config.log_file = optarg;
181 break; 208 break;
182 case 'e': /* ups name */ 209 case 'e': /* ups name */
183 expire_minutes = atoi(optarg); 210 result.config.expire_minutes = atoi(optarg);
184 break; 211 break;
185 case 'a': /* port */ 212 case 'a': /* port */
186 if (!strcmp(optarg, "MAX")) 213 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
187 use_average = false;
188 else
189 use_average = true;
190 break; 214 break;
191 case 'v': 215 case 'v':
192 variable_number = atoi(optarg); 216 result.config.variable_number = atoi(optarg);
193 if (variable_number < 1 || variable_number > 2) 217 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
194 usage4(_("Invalid variable number")); 218 usage4(_("Invalid variable number"));
219 }
195 break; 220 break;
196 case 'w': /* critical time threshold */ 221 case 'w': /* critical time threshold */
197 value_warning_threshold = strtoul(optarg, NULL, 10); 222 result.config.value_warning_threshold_set = true;
223 result.config.value_warning_threshold = strtoul(optarg, NULL, 10);
198 break; 224 break;
199 case 'c': /* warning time threshold */ 225 case 'c': /* warning time threshold */
200 value_critical_threshold = strtoul(optarg, NULL, 10); 226 result.config.value_critical_threshold_set = true;
227 result.config.value_critical_threshold = strtoul(optarg, NULL, 10);
201 break; 228 break;
202 case 'l': /* label */ 229 case 'l': /* label */
203 label = optarg; 230 result.config.label = optarg;
204 break; 231 break;
205 case 'u': /* timeout */ 232 case 'u': /* timeout */
206 units = optarg; 233 result.config.units = optarg;
207 break; 234 break;
208 case 'V': /* version */ 235 case 'V': /* version */
209 print_revision(progname, NP_VERSION); 236 print_revision(progname, NP_VERSION);
@@ -217,63 +244,71 @@ int process_arguments(int argc, char **argv) {
217 } 244 }
218 245
219 option_char = optind; 246 option_char = optind;
220 if (log_file == NULL && argc > option_char) { 247 if (result.config.log_file == NULL && argc > option_char) {
221 log_file = argv[option_char++]; 248 result.config.log_file = argv[option_char++];
222 } 249 }
223 250
224 if (expire_minutes <= 0 && argc > option_char) { 251 if (result.config.expire_minutes <= 0 && argc > option_char) {
225 if (is_intpos(argv[option_char])) 252 if (is_intpos(argv[option_char])) {
226 expire_minutes = atoi(argv[option_char++]); 253 result.config.expire_minutes = atoi(argv[option_char++]);
227 else 254 } else {
228 die(STATE_UNKNOWN, _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), argv[option_char], progname); 255 die(STATE_UNKNOWN,
256 _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"),
257 argv[option_char], progname);
258 }
229 } 259 }
230 260
231 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { 261 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
232 use_average = false; 262 result.config.use_average = false;
233 option_char++; 263 option_char++;
234 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { 264 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
235 use_average = true; 265 result.config.use_average = true;
236 option_char++; 266 option_char++;
237 } 267 }
238 268
239 if (argc > option_char && variable_number == -1) { 269 if (argc > option_char && result.config.variable_number == -1) {
240 variable_number = atoi(argv[option_char++]); 270 result.config.variable_number = atoi(argv[option_char++]);
241 if (variable_number < 1 || variable_number > 2) { 271 if (result.config.variable_number < 1 || result.config.variable_number > 2) {
242 printf("%s :", argv[option_char]); 272 printf("%s :", argv[option_char]);
243 usage(_("Invalid variable number\n")); 273 usage(_("Invalid variable number\n"));
244 } 274 }
245 } 275 }
246 276
247 if (argc > option_char && value_warning_threshold == 0) { 277 if (argc > option_char && !result.config.value_warning_threshold_set) {
248 value_warning_threshold = strtoul(argv[option_char++], NULL, 10); 278 result.config.value_warning_threshold_set = true;
279 result.config.value_warning_threshold = strtoul(argv[option_char++], NULL, 10);
249 } 280 }
250 281
251 if (argc > option_char && value_critical_threshold == 0) { 282 if (argc > option_char && !result.config.value_critical_threshold_set) {
252 value_critical_threshold = strtoul(argv[option_char++], NULL, 10); 283 result.config.value_critical_threshold_set = true;
284 result.config.value_critical_threshold = strtoul(argv[option_char++], NULL, 10);
253 } 285 }
254 286
255 if (argc > option_char && strlen(label) == 0) { 287 if (argc > option_char && strlen(result.config.label) == 0) {
256 label = argv[option_char++]; 288 result.config.label = argv[option_char++];
257 } 289 }
258 290
259 if (argc > option_char && strlen(units) == 0) { 291 if (argc > option_char && strlen(result.config.units) == 0) {
260 units = argv[option_char++]; 292 result.config.units = argv[option_char++];
261 } 293 }
262 294
263 return validate_arguments(); 295 return validate_arguments(result);
264} 296}
265 297
266int validate_arguments(void) { 298check_mrtg_config_wrapper validate_arguments(check_mrtg_config_wrapper config_wrapper) {
267 if (variable_number == -1) 299 if (config_wrapper.config.variable_number == -1) {
268 usage4(_("You must supply the variable number")); 300 usage4(_("You must supply the variable number"));
301 }
269 302
270 if (label == NULL) 303 if (config_wrapper.config.label == NULL) {
271 label = strdup("value"); 304 config_wrapper.config.label = strdup("value");
305 }
272 306
273 if (units == NULL) 307 if (config_wrapper.config.units == NULL) {
274 units = strdup(""); 308 config_wrapper.config.units = strdup("");
309 }
275 310
276 return OK; 311 return config_wrapper;
277} 312}
278 313
279void print_help(void) { 314void print_help(void) {
@@ -311,25 +346,32 @@ void print_help(void) {
311 printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); 346 printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")"));
312 347
313 printf("\n"); 348 printf("\n");
314 printf(" %s\n", _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If")); 349 printf(" %s\n",
350 _("If the value exceeds the <vwl> threshold, a WARNING status is returned. If"));
315 printf(" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If")); 351 printf(" %s\n", _("the value exceeds the <vcl> threshold, a CRITICAL status is returned. If"));
316 printf(" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING")); 352 printf(" %s\n", _("the data in the log file is older than <expire_minutes> old, a WARNING"));
317 printf(" %s\n", _("status is returned and a warning message is printed.")); 353 printf(" %s\n", _("status is returned and a warning message is printed."));
318 354
319 printf("\n"); 355 printf("\n");
320 printf(" %s\n", _("This plugin is useful for monitoring MRTG data that does not correspond to")); 356 printf(" %s\n",
321 printf(" %s\n", _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); 357 _("This plugin is useful for monitoring MRTG data that does not correspond to"));
322 printf(" %s\n", _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); 358 printf(" %s\n",
323 printf(" %s\n", _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); 359 _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth)."));
360 printf(" %s\n",
361 _("It can be used to monitor any kind of data that MRTG is monitoring - errors,"));
362 printf(" %s\n",
363 _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows"));
324 printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); 364 printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and"));
325 printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); 365 printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well."));
326 366
327 printf("%s\n", _("Notes:")); 367 printf("%s\n", _("Notes:"));
328 printf(" %s\n", _("- This plugin only monitors one of the two variables stored in the MRTG log")); 368 printf(" %s\n",
369 _("- This plugin only monitors one of the two variables stored in the MRTG log"));
329 printf(" %s\n", _("file. If you want to monitor both values you will have to define two")); 370 printf(" %s\n", _("file. If you want to monitor both values you will have to define two"));
330 printf(" %s\n", _("commands with different values for the <variable> argument. Of course,")); 371 printf(" %s\n", _("commands with different values for the <variable> argument. Of course,"));
331 printf(" %s\n", _("you can always hack the code to make this plugin work for you...")); 372 printf(" %s\n", _("you can always hack the code to make this plugin work for you..."));
332 printf(" %s\n", _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); 373 printf(" %s\n",
374 _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from"));
333 printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); 375 printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html");
334 376
335 printf(UT_SUPPORT); 377 printf(UT_SUPPORT);
diff --git a/plugins/check_mrtg.d/config.h b/plugins/check_mrtg.d/config.h
new file mode 100644
index 00000000..96b849a2
--- /dev/null
+++ b/plugins/check_mrtg.d/config.h
@@ -0,0 +1,36 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7typedef struct {
8 bool use_average;
9 int variable_number;
10 int expire_minutes;
11 char *label;
12 char *units;
13 char *log_file;
14
15 bool value_warning_threshold_set;
16 unsigned long value_warning_threshold;
17 bool value_critical_threshold_set;
18 unsigned long value_critical_threshold;
19} check_mrtg_config;
20
21check_mrtg_config check_mrtg_config_init() {
22 check_mrtg_config tmp = {
23 .use_average = true,
24 .variable_number = -1,
25 .expire_minutes = 0,
26 .label = NULL,
27 .units = NULL,
28 .log_file = NULL,
29
30 .value_warning_threshold_set = false,
31 .value_warning_threshold = 0,
32 .value_critical_threshold_set = false,
33 .value_critical_threshold = 0,
34 };
35 return tmp;
36}
diff --git a/plugins/check_mrtgtraf.c b/plugins/check_mrtgtraf.c
index e5a2e2ad..10ce936f 100644
--- a/plugins/check_mrtgtraf.c
+++ b/plugins/check_mrtgtraf.c
@@ -29,25 +29,23 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "common.h"
33#include "utils.h"
34
35const char *progname = "check_mrtgtraf"; 32const char *progname = "check_mrtgtraf";
36const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
37const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
38 35
39static int process_arguments(int /*argc*/, char ** /*argv*/); 36#include "check_mrtgtraf.d/config.h"
37#include "common.h"
38#include "utils.h"
39
40typedef struct {
41 int errorcode;
42 check_mrtgtraf_config config;
43} check_mrtgtraf_config_wrapper;
44
45static check_mrtgtraf_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
40static void print_help(void); 46static void print_help(void);
41void print_usage(void); 47void print_usage(void);
42 48
43static char *log_file = NULL;
44static int expire_minutes = -1;
45static bool use_average = true;
46static unsigned long incoming_warning_threshold = 0L;
47static unsigned long incoming_critical_threshold = 0L;
48static unsigned long outgoing_warning_threshold = 0L;
49static unsigned long outgoing_critical_threshold = 0L;
50
51int main(int argc, char **argv) { 49int main(int argc, char **argv) {
52 setlocale(LC_ALL, ""); 50 setlocale(LC_ALL, "");
53 bindtextdomain(PACKAGE, LOCALEDIR); 51 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -56,13 +54,18 @@ int main(int argc, char **argv) {
56 /* Parse extra opts if any */ 54 /* Parse extra opts if any */
57 argv = np_extra_opts(&argc, argv, progname); 55 argv = np_extra_opts(&argc, argv, progname);
58 56
59 if (process_arguments(argc, argv) == ERROR) 57 check_mrtgtraf_config_wrapper tmp_config = process_arguments(argc, argv);
58 if (tmp_config.errorcode == ERROR) {
60 usage4(_("Could not parse arguments")); 59 usage4(_("Could not parse arguments"));
60 }
61
62 const check_mrtgtraf_config config = tmp_config.config;
61 63
62 /* open the MRTG log file for reading */ 64 /* open the MRTG log file for reading */
63 FILE *mrtg_log_file_ptr = fopen(log_file, "r"); 65 FILE *mrtg_log_file_ptr = fopen(config.log_file, "r");
64 if (mrtg_log_file_ptr == NULL) 66 if (mrtg_log_file_ptr == NULL) {
65 usage4(_("Unable to open MRTG log file")); 67 usage4(_("Unable to open MRTG log file"));
68 }
66 69
67 time_t timestamp = 0L; 70 time_t timestamp = 0L;
68 char input_buffer[MAX_INPUT_BUFFER]; 71 char input_buffer[MAX_INPUT_BUFFER];
@@ -76,13 +79,15 @@ int main(int argc, char **argv) {
76 line++; 79 line++;
77 80
78 /* skip the first line of the log file */ 81 /* skip the first line of the log file */
79 if (line == 1) 82 if (line == 1) {
80 continue; 83 continue;
84 }
81 85
82 /* break out of read loop */ 86 /* break out of read loop */
83 /* if we've passed the number of entries we want to read */ 87 /* if we've passed the number of entries we want to read */
84 if (line > 2) 88 if (line > 2) {
85 break; 89 break;
90 }
86 91
87 /* grab the timestamp */ 92 /* grab the timestamp */
88 char *temp_buffer = strtok(input_buffer, " "); 93 char *temp_buffer = strtok(input_buffer, " ");
@@ -109,19 +114,22 @@ int main(int argc, char **argv) {
109 fclose(mrtg_log_file_ptr); 114 fclose(mrtg_log_file_ptr);
110 115
111 /* if we couldn't read enough data, return an unknown error */ 116 /* if we couldn't read enough data, return an unknown error */
112 if (line <= 2) 117 if (line <= 2) {
113 usage4(_("Unable to process MRTG log file")); 118 usage4(_("Unable to process MRTG log file"));
119 }
114 120
115 /* make sure the MRTG data isn't too old */ 121 /* make sure the MRTG data isn't too old */
116 time_t current_time; 122 time_t current_time;
117 time(&current_time); 123 time(&current_time);
118 if ((expire_minutes > 0) && (current_time - timestamp) > (expire_minutes * 60)) 124 if ((config.expire_minutes > 0) && (current_time - timestamp) > (config.expire_minutes * 60)) {
119 die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); 125 die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"),
126 (int)((current_time - timestamp) / 60));
127 }
120 128
121 unsigned long incoming_rate = 0L; 129 unsigned long incoming_rate = 0L;
122 unsigned long outgoing_rate = 0L; 130 unsigned long outgoing_rate = 0L;
123 /* else check the incoming/outgoing rates */ 131 /* else check the incoming/outgoing rates */
124 if (use_average) { 132 if (config.use_average) {
125 incoming_rate = average_incoming_rate; 133 incoming_rate = average_incoming_rate;
126 outgoing_rate = average_outgoing_rate; 134 outgoing_rate = average_outgoing_rate;
127 } else { 135 } else {
@@ -166,24 +174,31 @@ int main(int argc, char **argv) {
166 /* report outgoing traffic in MBytes/sec */ 174 /* report outgoing traffic in MBytes/sec */
167 else { 175 else {
168 strcpy(outgoing_speed_rating, "MB"); 176 strcpy(outgoing_speed_rating, "MB");
169 adjusted_outgoing_rate = (double)(outgoing_rate / 1024.0 / 1024.0); 177 adjusted_outgoing_rate = (outgoing_rate / 1024.0 / 1024.0);
170 } 178 }
171 179
172 int result = STATE_OK; 180 int result = STATE_OK;
173 if (incoming_rate > incoming_critical_threshold || outgoing_rate > outgoing_critical_threshold) { 181 if (incoming_rate > config.incoming_critical_threshold ||
182 outgoing_rate > config.outgoing_critical_threshold) {
174 result = STATE_CRITICAL; 183 result = STATE_CRITICAL;
175 } else if (incoming_rate > incoming_warning_threshold || outgoing_rate > outgoing_warning_threshold) { 184 } else if (incoming_rate > config.incoming_warning_threshold ||
185 outgoing_rate > config.outgoing_warning_threshold) {
176 result = STATE_WARNING; 186 result = STATE_WARNING;
177 } 187 }
178 188
179 char *error_message; 189 char *error_message;
180 xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), (use_average) ? _("Avg") : _("Max"), 190 xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"),
181 adjusted_incoming_rate, incoming_speed_rating, (use_average) ? _("Avg") : _("Max"), adjusted_outgoing_rate, 191 (config.use_average) ? _("Avg") : _("Max"), adjusted_incoming_rate,
182 outgoing_speed_rating, 192 incoming_speed_rating, (config.use_average) ? _("Avg") : _("Max"),
183 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, (int)incoming_warning_threshold, incoming_warning_threshold, 193 adjusted_outgoing_rate, outgoing_speed_rating,
184 (int)incoming_critical_threshold, incoming_critical_threshold, true, 0, false, 0), 194 fperfdata("in", adjusted_incoming_rate, incoming_speed_rating,
185 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, (int)outgoing_warning_threshold, outgoing_warning_threshold, 195 (int)config.incoming_warning_threshold, config.incoming_warning_threshold,
186 (int)outgoing_critical_threshold, outgoing_critical_threshold, true, 0, false, 0)); 196 (int)config.incoming_critical_threshold, config.incoming_critical_threshold,
197 true, 0, false, 0),
198 fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating,
199 (int)config.outgoing_warning_threshold, config.outgoing_warning_threshold,
200 (int)config.outgoing_critical_threshold, config.outgoing_critical_threshold,
201 true, 0, false, 0));
187 202
188 printf(_("Traffic %s - %s\n"), state_text(result), error_message); 203 printf(_("Traffic %s - %s\n"), state_text(result), error_message);
189 204
@@ -191,7 +206,7 @@ int main(int argc, char **argv) {
191} 206}
192 207
193/* process command-line arguments */ 208/* process command-line arguments */
194int process_arguments(int argc, char **argv) { 209check_mrtgtraf_config_wrapper process_arguments(int argc, char **argv) {
195 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, 210 static struct option longopts[] = {{"filename", required_argument, 0, 'F'},
196 {"expires", required_argument, 0, 'e'}, 211 {"expires", required_argument, 0, 'e'},
197 {"aggregation", required_argument, 0, 'a'}, 212 {"aggregation", required_argument, 0, 'a'},
@@ -201,44 +216,51 @@ int process_arguments(int argc, char **argv) {
201 {"help", no_argument, 0, 'h'}, 216 {"help", no_argument, 0, 'h'},
202 {0, 0, 0, 0}}; 217 {0, 0, 0, 0}};
203 218
204 if (argc < 2) 219 check_mrtgtraf_config_wrapper result = {
205 return ERROR; 220 .errorcode = OK,
221 .config = check_mrtgtraf_config_init(),
222 };
223 if (argc < 2) {
224 result.errorcode = ERROR;
225 return result;
226 }
206 227
207 for (int i = 1; i < argc; i++) { 228 for (int i = 1; i < argc; i++) {
208 if (strcmp("-to", argv[i]) == 0) 229 if (strcmp("-to", argv[i]) == 0) {
209 strcpy(argv[i], "-t"); 230 strcpy(argv[i], "-t");
210 else if (strcmp("-wt", argv[i]) == 0) 231 } else if (strcmp("-wt", argv[i]) == 0) {
211 strcpy(argv[i], "-w"); 232 strcpy(argv[i], "-w");
212 else if (strcmp("-ct", argv[i]) == 0) 233 } else if (strcmp("-ct", argv[i]) == 0) {
213 strcpy(argv[i], "-c"); 234 strcpy(argv[i], "-c");
235 }
214 } 236 }
215 237
216 int option_char; 238 int option_char;
217 int option = 0; 239 int option = 0;
218 while (1) { 240 while (true) {
219 option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option); 241 option_char = getopt_long(argc, argv, "hVF:e:a:c:w:", longopts, &option);
220 242
221 if (option_char == -1 || option_char == EOF) 243 if (option_char == -1 || option_char == EOF) {
222 break; 244 break;
245 }
223 246
224 switch (option_char) { 247 switch (option_char) {
225 case 'F': /* input file */ 248 case 'F': /* input file */
226 log_file = optarg; 249 result.config.log_file = optarg;
227 break; 250 break;
228 case 'e': /* expiration time */ 251 case 'e': /* expiration time */
229 expire_minutes = atoi(optarg); 252 result.config.expire_minutes = atoi(optarg);
230 break; 253 break;
231 case 'a': /* aggregation (AVE or MAX) */ 254 case 'a': /* aggregation (AVE or MAX) */
232 if (!strcmp(optarg, "MAX")) 255 result.config.use_average = (bool)(strcmp(optarg, "MAX"));
233 use_average = false;
234 else
235 use_average = true;
236 break; 256 break;
237 case 'c': /* warning threshold */ 257 case 'c': /* warning threshold */
238 sscanf(optarg, "%lu,%lu", &incoming_critical_threshold, &outgoing_critical_threshold); 258 sscanf(optarg, "%lu,%lu", &result.config.incoming_critical_threshold,
259 &result.config.outgoing_critical_threshold);
239 break; 260 break;
240 case 'w': /* critical threshold */ 261 case 'w': /* critical threshold */
241 sscanf(optarg, "%lu,%lu", &incoming_warning_threshold, &outgoing_warning_threshold); 262 sscanf(optarg, "%lu,%lu", &result.config.incoming_warning_threshold,
263 &result.config.outgoing_warning_threshold);
242 break; 264 break;
243 case 'V': /* version */ 265 case 'V': /* version */
244 print_revision(progname, NP_VERSION); 266 print_revision(progname, NP_VERSION);
@@ -252,39 +274,39 @@ int process_arguments(int argc, char **argv) {
252 } 274 }
253 275
254 option_char = optind; 276 option_char = optind;
255 if (argc > option_char && log_file == NULL) { 277 if (argc > option_char && result.config.log_file == NULL) {
256 log_file = argv[option_char++]; 278 result.config.log_file = argv[option_char++];
257 } 279 }
258 280
259 if (argc > option_char && expire_minutes == -1) { 281 if (argc > option_char && result.config.expire_minutes == -1) {
260 expire_minutes = atoi(argv[option_char++]); 282 result.config.expire_minutes = atoi(argv[option_char++]);
261 } 283 }
262 284
263 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) { 285 if (argc > option_char && strcmp(argv[option_char], "MAX") == 0) {
264 use_average = false; 286 result.config.use_average = false;
265 option_char++; 287 option_char++;
266 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) { 288 } else if (argc > option_char && strcmp(argv[option_char], "AVG") == 0) {
267 use_average = true; 289 result.config.use_average = true;
268 option_char++; 290 option_char++;
269 } 291 }
270 292
271 if (argc > option_char && incoming_warning_threshold == 0) { 293 if (argc > option_char && result.config.incoming_warning_threshold == 0) {
272 incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10); 294 result.config.incoming_warning_threshold = strtoul(argv[option_char++], NULL, 10);
273 } 295 }
274 296
275 if (argc > option_char && incoming_critical_threshold == 0) { 297 if (argc > option_char && result.config.incoming_critical_threshold == 0) {
276 incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10); 298 result.config.incoming_critical_threshold = strtoul(argv[option_char++], NULL, 10);
277 } 299 }
278 300
279 if (argc > option_char && outgoing_warning_threshold == 0) { 301 if (argc > option_char && result.config.outgoing_warning_threshold == 0) {
280 outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10); 302 result.config.outgoing_warning_threshold = strtoul(argv[option_char++], NULL, 10);
281 } 303 }
282 304
283 if (argc > option_char && outgoing_critical_threshold == 0) { 305 if (argc > option_char && result.config.outgoing_critical_threshold == 0) {
284 outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10); 306 result.config.outgoing_critical_threshold = strtoul(argv[option_char++], NULL, 10);
285 } 307 }
286 308
287 return OK; 309 return result;
288} 310}
289 311
290void print_help(void) { 312void print_help(void) {
diff --git a/plugins/check_mrtgtraf.d/config.h b/plugins/check_mrtgtraf.d/config.h
new file mode 100644
index 00000000..94929ff7
--- /dev/null
+++ b/plugins/check_mrtgtraf.d/config.h
@@ -0,0 +1,30 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7typedef struct {
8 char *log_file;
9 int expire_minutes;
10 bool use_average;
11 unsigned long incoming_warning_threshold;
12 unsigned long incoming_critical_threshold;
13 unsigned long outgoing_warning_threshold;
14 unsigned long outgoing_critical_threshold;
15
16} check_mrtgtraf_config;
17
18check_mrtgtraf_config check_mrtgtraf_config_init() {
19 check_mrtgtraf_config tmp = {
20 .log_file = NULL,
21 .expire_minutes = -1,
22 .use_average = true,
23
24 .incoming_warning_threshold = 0,
25 .incoming_critical_threshold = 0,
26 .outgoing_warning_threshold = 0,
27 .outgoing_critical_threshold = 0,
28 };
29 return tmp;
30}
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c
index 8a73772d..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-2024 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-2024"; 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
48static char *db_user = NULL;
49static char *db_host = NULL;
50static char *db_socket = NULL;
51static char *db_pass = NULL;
52static char *db = NULL;
53static char *ca_cert = NULL;
54static char *ca_dir = NULL;
55static char *cert = NULL;
56static char *key = NULL;
57static char *ciphers = NULL;
58static bool ssl = false;
59static char *opt_file = NULL;
60static char *opt_group = NULL;
61static unsigned int db_port = MYSQL_PORT;
62static bool check_slave = false;
63static bool ignore_auth = false;
64static int verbose = 0; 49static int verbose = 0;
65 50
66static double warning_time = 0;
67static double critical_time = 0;
68
69#define LENGTH_METRIC_UNIT 6 51#define LENGTH_METRIC_UNIT 6
70static const char *metric_unit[LENGTH_METRIC_UNIT] = { 52static const char *metric_unit[LENGTH_METRIC_UNIT] = {
71 "Open_files", 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
94static thresholds *my_threshold = NULL; 71typedef struct {
95 72 int errorcode;
96static int process_arguments (int, char **); 73 check_mysql_config config;
97static int validate_arguments (void); 74} check_mysql_config_wrapper;
98static void 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) {
104 81 setlocale(LC_ALL, "");
105 MYSQL mysql; 82 bindtextdomain(PACKAGE, LOCALEDIR);
106 MYSQL_RES *res; 83 textdomain(PACKAGE);
107 MYSQL_ROW row;
108 84
109 /* should be status */ 85 /* Parse extra opts if any */
86 argv = np_extra_opts(&argc, argv, progname);
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 = true; /* 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 = true; /* 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 79b6e2f4..c7e84deb 100644
--- a/plugins/check_mysql_query.c
+++ b/plugins/check_mysql_query.c
@@ -37,27 +37,22 @@ const char *email = "devel@monitoring-plugins.org";
37#include "utils.h" 37#include "utils.h"
38#include "utils_base.h" 38#include "utils_base.h"
39#include "netutils.h" 39#include "netutils.h"
40#include "check_mysql_query.d/config.h"
40 41
41#include <mysql.h> 42#include <mysql.h>
42#include <errmsg.h> 43#include <errmsg.h>
43 44
44static char *db_user = NULL; 45typedef struct {
45static char *db_host = NULL; 46 int errorcode;
46static char *db_socket = NULL; 47 check_mysql_query_config config;
47static char *db_pass = NULL; 48} check_mysql_query_config_wrapper;
48static char *db = NULL; 49static check_mysql_query_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static char *opt_file = NULL; 50static check_mysql_query_config_wrapper
50static char *opt_group = NULL; 51 validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/);
51static unsigned int db_port = MYSQL_PORT;
52
53static int process_arguments(int /*argc*/, char ** /*argv*/);
54static int validate_arguments(void);
55static void print_help(void); 52static void print_help(void);
56void print_usage(void); 53void print_usage(void);
57 54
58static char *sql_query = NULL;
59static int verbose = 0; 55static int verbose = 0;
60static thresholds *my_thresholds = NULL;
61 56
62int main(int argc, char **argv) { 57int main(int argc, char **argv) {
63 setlocale(LC_ALL, ""); 58 setlocale(LC_ALL, "");
@@ -67,39 +62,47 @@ int main(int argc, char **argv) {
67 /* Parse extra opts if any */ 62 /* Parse extra opts if any */
68 argv = np_extra_opts(&argc, argv, progname); 63 argv = np_extra_opts(&argc, argv, progname);
69 64
70 if (process_arguments(argc, argv) == ERROR) 65 check_mysql_query_config_wrapper tmp_config = process_arguments(argc, argv);
66 if (tmp_config.errorcode == ERROR) {
71 usage4(_("Could not parse arguments")); 67 usage4(_("Could not parse arguments"));
68 }
69
70 const check_mysql_query_config config = tmp_config.config;
72 71
73 MYSQL mysql; 72 MYSQL mysql;
74 /* initialize mysql */ 73 /* initialize mysql */
75 mysql_init(&mysql); 74 mysql_init(&mysql);
76 75
77 if (opt_file != NULL) 76 if (config.opt_file != NULL) {
78 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, opt_file); 77 mysql_options(&mysql, MYSQL_READ_DEFAULT_FILE, config.opt_file);
78 }
79 79
80 if (opt_group != NULL) 80 if (config.opt_group != NULL) {
81 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, opt_group); 81 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, config.opt_group);
82 else 82 } else {
83 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); 83 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
84 }
84 85
85 /* establish a connection to the server and error checking */ 86 /* establish a connection to the server and error checking */
86 if (!mysql_real_connect(&mysql, db_host, db_user, db_pass, db, db_port, db_socket, 0)) { 87 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
87 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) 88 config.db_port, config.db_socket, 0)) {
89 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
88 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 90 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
89 else if (mysql_errno(&mysql) == CR_VERSION_ERROR) 91 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
90 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 92 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
91 else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) 93 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
92 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 94 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
93 else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) 95 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
94 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 96 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
95 else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) 97 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
96 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 98 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql));
97 else 99 } else {
98 die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql)); 100 die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql));
101 }
99 } 102 }
100 103
101 char *error = NULL; 104 char *error = NULL;
102 if (mysql_query(&mysql, sql_query) != 0) { 105 if (mysql_query(&mysql, config.sql_query) != 0) {
103 error = strdup(mysql_error(&mysql)); 106 error = strdup(mysql_error(&mysql));
104 mysql_close(&mysql); 107 mysql_close(&mysql);
105 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); 108 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error);
@@ -140,10 +143,11 @@ int main(int argc, char **argv) {
140 /* close the connection */ 143 /* close the connection */
141 mysql_close(&mysql); 144 mysql_close(&mysql);
142 145
143 if (verbose >= 3) 146 if (verbose >= 3) {
144 printf("mysql result: %f\n", value); 147 printf("mysql result: %f\n", value);
148 }
145 149
146 int status = get_status(value, my_thresholds); 150 int status = get_status(value, config.my_thresholds);
147 151
148 if (status == STATE_OK) { 152 if (status == STATE_OK) {
149 printf("QUERY %s: ", _("OK")); 153 printf("QUERY %s: ", _("OK"));
@@ -152,26 +156,44 @@ int main(int argc, char **argv) {
152 } else if (status == STATE_CRITICAL) { 156 } else if (status == STATE_CRITICAL) {
153 printf("QUERY %s: ", _("CRITICAL")); 157 printf("QUERY %s: ", _("CRITICAL"));
154 } 158 }
155 printf(_("'%s' returned %f | %s"), sql_query, value, 159 printf(_("'%s' returned %f | %s"), config.sql_query, value,
156 fperfdata("result", value, "", my_thresholds->warning ? true : false, my_thresholds->warning ? my_thresholds->warning->end : 0, 160 fperfdata("result", value, "", config.my_thresholds->warning,
157 my_thresholds->critical ? true : false, my_thresholds->critical ? my_thresholds->critical->end : 0, false, 0, false, 161 config.my_thresholds->warning ? config.my_thresholds->warning->end : 0,
158 0)); 162 config.my_thresholds->critical,
163 config.my_thresholds->critical ? config.my_thresholds->critical->end : 0,
164 false, 0, false, 0));
159 printf("\n"); 165 printf("\n");
160 166
161 return status; 167 return status;
162} 168}
163 169
164/* process command-line arguments */ 170/* process command-line arguments */
165int process_arguments(int argc, char **argv) { 171check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
166 static struct option longopts[] = { 172 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
167 {"hostname", required_argument, 0, 'H'}, {"socket", required_argument, 0, 's'}, {"database", required_argument, 0, 'd'}, 173 {"socket", required_argument, 0, 's'},
168 {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, {"file", required_argument, 0, 'f'}, 174 {"database", required_argument, 0, 'd'},
169 {"group", required_argument, 0, 'g'}, {"port", required_argument, 0, 'P'}, {"verbose", no_argument, 0, 'v'}, 175 {"username", required_argument, 0, 'u'},
170 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"query", required_argument, 0, 'q'}, 176 {"password", required_argument, 0, 'p'},
171 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {0, 0, 0, 0}}; 177 {"file", required_argument, 0, 'f'},
172 178 {"group", required_argument, 0, 'g'},
173 if (argc < 1) 179 {"port", required_argument, 0, 'P'},
174 return ERROR; 180 {"verbose", no_argument, 0, 'v'},
181 {"version", no_argument, 0, 'V'},
182 {"help", no_argument, 0, 'h'},
183 {"query", required_argument, 0, 'q'},
184 {"warning", required_argument, 0, 'w'},
185 {"critical", required_argument, 0, 'c'},
186 {0, 0, 0, 0}};
187
188 check_mysql_query_config_wrapper result = {
189 .errorcode = OK,
190 .config = check_mysql_query_config_init(),
191 };
192
193 if (argc < 1) {
194 result.errorcode = ERROR;
195 return result;
196 }
175 197
176 char *warning = NULL; 198 char *warning = NULL;
177 char *critical = NULL; 199 char *critical = NULL;
@@ -180,28 +202,29 @@ int process_arguments(int argc, char **argv) {
180 int option = 0; 202 int option = 0;
181 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); 203 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option);
182 204
183 if (option_char == -1 || option_char == EOF) 205 if (option_char == -1 || option_char == EOF) {
184 break; 206 break;
207 }
185 208
186 switch (option_char) { 209 switch (option_char) {
187 case 'H': /* hostname */ 210 case 'H': /* hostname */
188 if (is_host(optarg)) { 211 if (is_host(optarg)) {
189 db_host = optarg; 212 result.config.db_host = optarg;
190 } else { 213 } else {
191 usage2(_("Invalid hostname/address"), optarg); 214 usage2(_("Invalid hostname/address"), optarg);
192 } 215 }
193 break; 216 break;
194 case 's': /* socket */ 217 case 's': /* socket */
195 db_socket = optarg; 218 result.config.db_socket = optarg;
196 break; 219 break;
197 case 'd': /* database */ 220 case 'd': /* database */
198 db = optarg; 221 result.config.db = optarg;
199 break; 222 break;
200 case 'u': /* username */ 223 case 'u': /* username */
201 db_user = optarg; 224 result.config.db_user = optarg;
202 break; 225 break;
203 case 'p': /* authentication information: password */ 226 case 'p': /* authentication information: password */
204 db_pass = strdup(optarg); 227 result.config.db_pass = strdup(optarg);
205 228
206 /* Delete the password from process list */ 229 /* Delete the password from process list */
207 while (*optarg != '\0') { 230 while (*optarg != '\0') {
@@ -210,13 +233,13 @@ int process_arguments(int argc, char **argv) {
210 } 233 }
211 break; 234 break;
212 case 'f': /* client options file */ 235 case 'f': /* client options file */
213 opt_file = optarg; 236 result.config.opt_file = optarg;
214 break; 237 break;
215 case 'g': /* client options group */ 238 case 'g': /* client options group */
216 opt_group = optarg; 239 result.config.opt_group = optarg;
217 break; 240 break;
218 case 'P': /* critical time threshold */ 241 case 'P': /* critical time threshold */
219 db_port = atoi(optarg); 242 result.config.db_port = atoi(optarg);
220 break; 243 break;
221 case 'v': 244 case 'v':
222 verbose++; 245 verbose++;
@@ -228,7 +251,7 @@ int process_arguments(int argc, char **argv) {
228 print_help(); 251 print_help();
229 exit(STATE_UNKNOWN); 252 exit(STATE_UNKNOWN);
230 case 'q': 253 case 'q':
231 xasprintf(&sql_query, "%s", optarg); 254 xasprintf(&result.config.sql_query, "%s", optarg);
232 break; 255 break;
233 case 'w': 256 case 'w':
234 warning = optarg; 257 warning = optarg;
@@ -241,25 +264,30 @@ int process_arguments(int argc, char **argv) {
241 } 264 }
242 } 265 }
243 266
244 set_thresholds(&my_thresholds, warning, critical); 267 set_thresholds(&result.config.my_thresholds, warning, critical);
245 268
246 return validate_arguments(); 269 return validate_arguments(result);
247} 270}
248 271
249int validate_arguments(void) { 272check_mysql_query_config_wrapper
250 if (sql_query == NULL) 273validate_arguments(check_mysql_query_config_wrapper config_wrapper) {
274 if (config_wrapper.config.sql_query == NULL) {
251 usage("Must specify a SQL query to run"); 275 usage("Must specify a SQL query to run");
276 }
252 277
253 if (db_user == NULL) 278 if (config_wrapper.config.db_user == NULL) {
254 db_user = strdup(""); 279 config_wrapper.config.db_user = strdup("");
280 }
255 281
256 if (db_host == NULL) 282 if (config_wrapper.config.db_host == NULL) {
257 db_host = strdup(""); 283 config_wrapper.config.db_host = strdup("");
284 }
258 285
259 if (db == NULL) 286 if (config_wrapper.config.db == NULL) {
260 db = strdup(""); 287 config_wrapper.config.db = strdup("");
288 }
261 289
262 return OK; 290 return config_wrapper;
263} 291}
264 292
265void print_help(void) { 293void print_help(void) {
diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h
new file mode 100644
index 00000000..be019160
--- /dev/null
+++ b/plugins/check_mysql_query.d/config.h
@@ -0,0 +1,36 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <mysql.h>
6
7typedef struct {
8 char *db_host;
9 char *db_socket;
10 char *db;
11 char *db_user;
12 char *db_pass;
13 char *opt_file;
14 char *opt_group;
15 unsigned int db_port;
16
17 char *sql_query;
18 thresholds *my_thresholds;
19} check_mysql_query_config;
20
21check_mysql_query_config check_mysql_query_config_init() {
22 check_mysql_query_config tmp = {
23 .db_host = NULL,
24 .db_socket = NULL,
25 .db = NULL,
26 .db_user = NULL,
27 .db_pass = NULL,
28 .opt_file = NULL,
29 .opt_group = NULL,
30 .db_port = MYSQL_PORT,
31
32 .sql_query = NULL,
33 .my_thresholds = NULL,
34 };
35 return tmp;
36}
diff --git a/plugins/check_nagios.c b/plugins/check_nagios.c
index 48629be3..a46dc1ed 100644
--- a/plugins/check_nagios.c
+++ b/plugins/check_nagios.c
@@ -39,47 +39,20 @@ const char *email = "devel@monitoring-plugins.org";
39#include "common.h" 39#include "common.h"
40#include "runcmd.h" 40#include "runcmd.h"
41#include "utils.h" 41#include "utils.h"
42 42#include "states.h"
43static int process_arguments(int /*argc*/, char ** /*argv*/); 43#include "check_nagios.d/config.h"
44
45typedef struct {
46 int errorcode;
47 check_nagios_config config;
48} check_nagios_config_wrapper;
49static check_nagios_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
44static void print_help(void); 50static void print_help(void);
45void print_usage(void); 51void print_usage(void);
46 52
47static char *status_log = NULL;
48static char *process_string = NULL;
49static int expire_minutes = 0;
50
51static int verbose = 0; 53static int verbose = 0;
52 54
53int main(int argc, char **argv) { 55int main(int argc, char **argv) {
54 int result = STATE_UNKNOWN;
55 char input_buffer[MAX_INPUT_BUFFER];
56 unsigned long latest_entry_time = 0L;
57 unsigned long temp_entry_time = 0L;
58 int proc_entries = 0;
59 time_t current_time;
60 char *temp_ptr;
61 FILE *fp;
62 int procuid = 0;
63 int procpid = 0;
64 int procppid = 0;
65 int procvsz = 0;
66 int procrss = 0;
67 float procpcpu = 0;
68 char procstat[8];
69#ifdef PS_USES_PROCETIME
70 char procetime[MAX_INPUT_BUFFER];
71#endif /* PS_USES_PROCETIME */
72 char procprog[MAX_INPUT_BUFFER];
73 char *procargs;
74 int pos;
75 int cols;
76 int expected_cols = PS_COLS - 1;
77 const char *zombie = "Z";
78 char *temp_string;
79 output chld_out;
80 output chld_err;
81 size_t i;
82
83 setlocale(LC_ALL, ""); 56 setlocale(LC_ALL, "");
84 bindtextdomain(PACKAGE, LOCALEDIR); 57 bindtextdomain(PACKAGE, LOCALEDIR);
85 textdomain(PACKAGE); 58 textdomain(PACKAGE);
@@ -87,8 +60,13 @@ int main(int argc, char **argv) {
87 /* Parse extra opts if any */ 60 /* Parse extra opts if any */
88 argv = np_extra_opts(&argc, argv, progname); 61 argv = np_extra_opts(&argc, argv, progname);
89 62
90 if (process_arguments(argc, argv) == ERROR) 63 check_nagios_config_wrapper tmp_config = process_arguments(argc, argv);
64
65 if (tmp_config.errorcode == ERROR) {
91 usage_va(_("Could not parse arguments")); 66 usage_va(_("Could not parse arguments"));
67 }
68
69 const check_nagios_config config = tmp_config.config;
92 70
93 /* Set signal handling and alarm timeout */ 71 /* Set signal handling and alarm timeout */
94 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
@@ -99,13 +77,17 @@ int main(int argc, char **argv) {
99 alarm(timeout_interval); 77 alarm(timeout_interval);
100 78
101 /* open the status log */ 79 /* open the status log */
102 fp = fopen(status_log, "r"); 80 FILE *log_file = fopen(config.status_log, "r");
103 if (fp == NULL) { 81 if (log_file == NULL) {
104 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!")); 82 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot open status log for reading!"));
105 } 83 }
106 84
85 unsigned long latest_entry_time = 0L;
86 unsigned long temp_entry_time = 0L;
87 char input_buffer[MAX_INPUT_BUFFER];
88 char *temp_ptr;
107 /* get the date/time of the last item updated in the log */ 89 /* get the date/time of the last item updated in the log */
108 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, fp)) { 90 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, log_file)) {
109 if ((temp_ptr = strstr(input_buffer, "created=")) != NULL) { 91 if ((temp_ptr = strstr(input_buffer, "created=")) != NULL) {
110 temp_entry_time = strtoul(temp_ptr + 8, NULL, 10); 92 temp_entry_time = strtoul(temp_ptr + 8, NULL, 10);
111 latest_entry_time = temp_entry_time; 93 latest_entry_time = temp_entry_time;
@@ -113,22 +95,44 @@ int main(int argc, char **argv) {
113 } 95 }
114 if ((temp_ptr = strtok(input_buffer, "]")) != NULL) { 96 if ((temp_ptr = strtok(input_buffer, "]")) != NULL) {
115 temp_entry_time = strtoul(temp_ptr + 1, NULL, 10); 97 temp_entry_time = strtoul(temp_ptr + 1, NULL, 10);
116 if (temp_entry_time > latest_entry_time) 98 if (temp_entry_time > latest_entry_time) {
117 latest_entry_time = temp_entry_time; 99 latest_entry_time = temp_entry_time;
100 }
118 } 101 }
119 } 102 }
120 fclose(fp); 103 fclose(log_file);
121 104
122 if (verbose >= 2) 105 if (verbose >= 2) {
123 printf("command: %s\n", PS_COMMAND); 106 printf("command: %s\n", PS_COMMAND);
107 }
124 108
125 /* run the command to check for the Nagios process.. */ 109 /* run the command to check for the Nagios process.. */
126 if ((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) 110 mp_state_enum result = STATE_UNKNOWN;
111 output chld_out;
112 output chld_err;
113 if ((result = np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0)) != 0) {
127 result = STATE_WARNING; 114 result = STATE_WARNING;
115 }
128 116
117 int procuid = 0;
118 int procpid = 0;
119 int procppid = 0;
120 int procvsz = 0;
121 int procrss = 0;
122 int proc_entries = 0;
123 float procpcpu = 0;
124 char procstat[8];
125 char procprog[MAX_INPUT_BUFFER];
126 char *procargs;
127#ifdef PS_USES_PROCETIME
128 char procetime[MAX_INPUT_BUFFER];
129#endif /* PS_USES_PROCETIME */
130 int pos;
131 int expected_cols = PS_COLS - 1;
132 const char *zombie = "Z";
129 /* count the number of matching Nagios processes... */ 133 /* count the number of matching Nagios processes... */
130 for (i = 0; i < chld_out.lines; i++) { 134 for (size_t i = 0; i < chld_out.lines; i++) {
131 cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST); 135 int cols = sscanf(chld_out.line[i], PS_FORMAT, PS_VARLIST);
132 /* Zombie processes do not give a procprog command */ 136 /* Zombie processes do not give a procprog command */
133 if (cols == (expected_cols - 1) && strstr(procstat, zombie)) { 137 if (cols == (expected_cols - 1) && strstr(procstat, zombie)) {
134 cols = expected_cols; 138 cols = expected_cols;
@@ -142,14 +146,14 @@ int main(int argc, char **argv) {
142 strip(procargs); 146 strip(procargs);
143 147
144 /* Some ps return full pathname for command. This removes path */ 148 /* Some ps return full pathname for command. This removes path */
145 temp_string = strtok((char *)procprog, "/"); 149 char *temp_string = strtok((char *)procprog, "/");
146 while (temp_string) { 150 while (temp_string) {
147 strcpy(procprog, temp_string); 151 strcpy(procprog, temp_string);
148 temp_string = strtok(NULL, "/"); 152 temp_string = strtok(NULL, "/");
149 } 153 }
150 154
151 /* May get empty procargs */ 155 /* May get empty procargs */
152 if (!strstr(procargs, argv[0]) && strstr(procargs, process_string) && strcmp(procargs, "")) { 156 if (!strstr(procargs, argv[0]) && strstr(procargs, config.process_string) && strcmp(procargs, "")) {
153 proc_entries++; 157 proc_entries++;
154 if (verbose >= 2) { 158 if (verbose >= 2) {
155 printf(_("Found process: %s %s\n"), procprog, procargs); 159 printf(_("Found process: %s %s\n"), procprog, procargs);
@@ -159,8 +163,9 @@ int main(int argc, char **argv) {
159 } 163 }
160 164
161 /* If we get anything on stderr, at least set warning */ 165 /* If we get anything on stderr, at least set warning */
162 if (chld_err.buflen) 166 if (chld_err.buflen) {
163 result = max_state(result, STATE_WARNING); 167 result = max_state(result, STATE_WARNING);
168 }
164 169
165 /* reset the alarm handler */ 170 /* reset the alarm handler */
166 alarm(0); 171 alarm(0);
@@ -173,8 +178,9 @@ int main(int argc, char **argv) {
173 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time")); 178 die(STATE_CRITICAL, "NAGIOS %s: %s\n", _("CRITICAL"), _("Cannot parse Nagios log file for valid time"));
174 } 179 }
175 180
181 time_t current_time;
176 time(&current_time); 182 time(&current_time);
177 if ((int)(current_time - latest_entry_time) > (expire_minutes * 60)) { 183 if ((int)(current_time - latest_entry_time) > (config.expire_minutes * 60)) {
178 result = STATE_WARNING; 184 result = STATE_WARNING;
179 } else { 185 } else {
180 result = STATE_OK; 186 result = STATE_OK;
@@ -187,39 +193,45 @@ int main(int argc, char **argv) {
187 (int)(current_time - latest_entry_time)); 193 (int)(current_time - latest_entry_time));
188 printf("\n"); 194 printf("\n");
189 195
190 return result; 196 exit(result);
191} 197}
192 198
193/* process command-line arguments */ 199/* process command-line arguments */
194int process_arguments(int argc, char **argv) { 200check_nagios_config_wrapper process_arguments(int argc, char **argv) {
195 int c;
196
197 int option = 0;
198 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, 201 static struct option longopts[] = {{"filename", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'},
199 {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'}, 202 {"command", required_argument, 0, 'C'}, {"timeout", optional_argument, 0, 't'},
200 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, 203 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
201 {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}}; 204 {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}};
202 205
203 if (argc < 2) 206 check_nagios_config_wrapper result = {
204 return ERROR; 207 .errorcode = OK,
208 .config = check_nagios_config_init(),
209 };
210 if (argc < 2) {
211 result.errorcode = ERROR;
212 return result;
213 }
205 214
206 if (!is_option(argv[1])) { 215 if (!is_option(argv[1])) {
207 status_log = argv[1]; 216 result.config.status_log = argv[1];
208 if (is_intnonneg(argv[2])) 217 if (is_intnonneg(argv[2])) {
209 expire_minutes = atoi(argv[2]); 218 result.config.expire_minutes = atoi(argv[2]);
210 else 219 } else {
211 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); 220 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
212 process_string = argv[3]; 221 }
213 return OK; 222 result.config.process_string = argv[3];
223 return result;
214 } 224 }
215 225
216 while (1) { 226 int option = 0;
217 c = getopt_long(argc, argv, "+hVvF:C:e:t:", longopts, &option); 227 while (true) {
228 int option_index = getopt_long(argc, argv, "+hVvF:C:e:t:", longopts, &option);
218 229
219 if (c == -1 || c == EOF || c == 1) 230 if (option_index == -1 || option_index == EOF || option_index == 1) {
220 break; 231 break;
232 }
221 233
222 switch (c) { 234 switch (option_index) {
223 case 'h': /* help */ 235 case 'h': /* help */
224 print_help(); 236 print_help();
225 exit(STATE_UNKNOWN); 237 exit(STATE_UNKNOWN);
@@ -227,22 +239,24 @@ int process_arguments(int argc, char **argv) {
227 print_revision(progname, NP_VERSION); 239 print_revision(progname, NP_VERSION);
228 exit(STATE_UNKNOWN); 240 exit(STATE_UNKNOWN);
229 case 'F': /* status log */ 241 case 'F': /* status log */
230 status_log = optarg; 242 result.config.status_log = optarg;
231 break; 243 break;
232 case 'C': /* command */ 244 case 'C': /* command */
233 process_string = optarg; 245 result.config.process_string = optarg;
234 break; 246 break;
235 case 'e': /* expiry time */ 247 case 'e': /* expiry time */
236 if (is_intnonneg(optarg)) 248 if (is_intnonneg(optarg)) {
237 expire_minutes = atoi(optarg); 249 result.config.expire_minutes = atoi(optarg);
238 else 250 } else {
239 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n")); 251 die(STATE_UNKNOWN, _("Expiration time must be an integer (seconds)\n"));
252 }
240 break; 253 break;
241 case 't': /* timeout */ 254 case 't': /* timeout */
242 if (is_intnonneg(optarg)) 255 if (is_intnonneg(optarg)) {
243 timeout_interval = atoi(optarg); 256 timeout_interval = atoi(optarg);
244 else 257 } else {
245 die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n")); 258 die(STATE_UNKNOWN, _("Timeout must be an integer (seconds)\n"));
259 }
246 break; 260 break;
247 case 'v': 261 case 'v':
248 verbose++; 262 verbose++;
@@ -252,13 +266,15 @@ int process_arguments(int argc, char **argv) {
252 } 266 }
253 } 267 }
254 268
255 if (status_log == NULL) 269 if (result.config.status_log == NULL) {
256 die(STATE_UNKNOWN, _("You must provide the status_log\n")); 270 die(STATE_UNKNOWN, _("You must provide the status_log\n"));
271 }
257 272
258 if (process_string == NULL) 273 if (result.config.process_string == NULL) {
259 die(STATE_UNKNOWN, _("You must provide a process string\n")); 274 die(STATE_UNKNOWN, _("You must provide a process string\n"));
275 }
260 276
261 return OK; 277 return result;
262} 278}
263 279
264void print_help(void) { 280void print_help(void) {
diff --git a/plugins/check_nagios.d/config.h b/plugins/check_nagios.d/config.h
new file mode 100644
index 00000000..efe139f9
--- /dev/null
+++ b/plugins/check_nagios.d/config.h
@@ -0,0 +1,19 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6typedef struct {
7 char *status_log;
8 char *process_string;
9 int expire_minutes;
10} check_nagios_config;
11
12check_nagios_config check_nagios_config_init() {
13 check_nagios_config tmp = {
14 .status_log = NULL,
15 .process_string = NULL,
16 .expire_minutes = 0,
17 };
18 return tmp;
19}
diff --git a/plugins/check_nt.c b/plugins/check_nt.c
index dec0b668..35ca92cd 100644
--- a/plugins/check_nt.c
+++ b/plugins/check_nt.c
@@ -13,7 +13,7 @@
13 * This plugin collects data from the NSClient service running on a 13 * This plugin collects data from the NSClient service running on a
14 * Windows NT/2000/XP/2003 server. 14 * Windows NT/2000/XP/2003 server.
15 * This plugin requires NSClient software to run on NT 15 * This plugin requires NSClient software to run on NT
16 * (http://nsclient.ready2run.nl/) 16 * (https://nsclient.org/)
17 * 17 *
18 * 18 *
19 * This program is free software: you can redistribute it and/or modify 19 * This program is free software: you can redistribute it and/or modify
@@ -39,82 +39,28 @@ const char *email = "devel@monitoring-plugins.org";
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42 42#include "check_nt.d/config.h"
43enum checkvars {
44 CHECK_NONE,
45 CHECK_CLIENTVERSION,
46 CHECK_CPULOAD,
47 CHECK_UPTIME,
48 CHECK_USEDDISKSPACE,
49 CHECK_SERVICESTATE,
50 CHECK_PROCSTATE,
51 CHECK_MEMUSE,
52 CHECK_COUNTER,
53 CHECK_FILEAGE,
54 CHECK_INSTANCES
55};
56 43
57enum { 44enum {
58 MAX_VALUE_LIST = 30, 45 MAX_VALUE_LIST = 30,
59 PORT = 1248
60}; 46};
61 47
62static char *server_address = NULL;
63static int server_port = PORT;
64static char *value_list = NULL;
65static char *req_password = NULL;
66static unsigned long lvalue_list[MAX_VALUE_LIST];
67static unsigned long warning_value = 0L;
68static unsigned long critical_value = 0L;
69static bool check_warning_value = false;
70static bool check_critical_value = false;
71static enum checkvars vars_to_check = CHECK_NONE;
72static bool show_all = false;
73
74static char recv_buffer[MAX_INPUT_BUFFER]; 48static char recv_buffer[MAX_INPUT_BUFFER];
75 49
76static void fetch_data(const char *address, int port, const char *sendb); 50static void fetch_data(const char *address, int port, const char *sendb);
77static int process_arguments(int /*argc*/, char ** /*argv*/); 51
52typedef struct {
53 int errorcode;
54 check_nt_config config;
55} check_nt_config_wrapper;
56static check_nt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57
78static void preparelist(char *string); 58static void preparelist(char *string);
79static bool strtoularray(unsigned long *array, char *string, const char *delim); 59static bool strtoularray(unsigned long *array, char *string, const char *delim);
80static void print_help(void); 60static void print_help(void);
81void print_usage(void); 61void print_usage(void);
82 62
83int main(int argc, char **argv) { 63int main(int argc, char **argv) {
84
85 /* should be int result = STATE_UNKNOWN; */
86
87 int return_code = STATE_UNKNOWN;
88 char *send_buffer = NULL;
89 char *output_message = NULL;
90 char *perfdata = NULL;
91 char *temp_string = NULL;
92 char *temp_string_perf = NULL;
93 char *description = NULL, *counter_unit = NULL;
94 char *minval = NULL, *maxval = NULL, *errcvt = NULL;
95 char *fds = NULL, *tds = NULL;
96 char *numstr;
97
98 double total_disk_space = 0;
99 double free_disk_space = 0;
100 double percent_used_space = 0;
101 double warning_used_space = 0;
102 double critical_used_space = 0;
103 double mem_commitLimit = 0;
104 double mem_commitByte = 0;
105 double fminval = 0, fmaxval = 0;
106 unsigned long utilization;
107 unsigned long uptime;
108 unsigned long age_in_minutes;
109 double counter_value = 0.0;
110 int offset = 0;
111 int updays = 0;
112 int uphours = 0;
113 int upminutes = 0;
114
115 bool isPercent = false;
116 bool allRight = false;
117
118 setlocale(LC_ALL, ""); 64 setlocale(LC_ALL, "");
119 bindtextdomain(PACKAGE, LOCALEDIR); 65 bindtextdomain(PACKAGE, LOCALEDIR);
120 textdomain(PACKAGE); 66 textdomain(PACKAGE);
@@ -122,8 +68,12 @@ int main(int argc, char **argv) {
122 /* Parse extra opts if any */ 68 /* Parse extra opts if any */
123 argv = np_extra_opts(&argc, argv, progname); 69 argv = np_extra_opts(&argc, argv, progname);
124 70
125 if (process_arguments(argc, argv) == ERROR) 71 check_nt_config_wrapper tmp_config = process_arguments(argc, argv);
72 if (tmp_config.errorcode == ERROR) {
126 usage4(_("Could not parse arguments")); 73 usage4(_("Could not parse arguments"));
74 }
75
76 const check_nt_config config = tmp_config.config;
127 77
128 /* initialize alarm signal handling */ 78 /* initialize alarm signal handling */
129 signal(SIGALRM, socket_timeout_alarm_handler); 79 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -131,54 +81,68 @@ int main(int argc, char **argv) {
131 /* set socket timeout */ 81 /* set socket timeout */
132 alarm(socket_timeout); 82 alarm(socket_timeout);
133 83
134 switch (vars_to_check) { 84 int return_code = STATE_UNKNOWN;
135 85 char *send_buffer = NULL;
86 char *output_message = NULL;
87 char *perfdata = NULL;
88 char *temp_string = NULL;
89 char *temp_string_perf = NULL;
90 char *description = NULL;
91 char *counter_unit = NULL;
92 char *errcvt = NULL;
93 unsigned long lvalue_list[MAX_VALUE_LIST];
94 switch (config.vars_to_check) {
136 case CHECK_CLIENTVERSION: 95 case CHECK_CLIENTVERSION:
137 96 xasprintf(&send_buffer, "%s&1", config.req_password);
138 xasprintf(&send_buffer, "%s&1", req_password); 97 fetch_data(config.server_address, config.server_port, send_buffer);
139 fetch_data(server_address, server_port, send_buffer); 98 if (config.value_list != NULL && strcmp(recv_buffer, config.value_list) != 0) {
140 if (value_list != NULL && strcmp(recv_buffer, value_list) != 0) { 99 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"),
141 xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"), recv_buffer, value_list); 100 recv_buffer, config.value_list);
142 return_code = STATE_WARNING; 101 return_code = STATE_WARNING;
143 } else { 102 } else {
144 xasprintf(&output_message, "%s", recv_buffer); 103 xasprintf(&output_message, "%s", recv_buffer);
145 return_code = STATE_OK; 104 return_code = STATE_OK;
146 } 105 }
147 break; 106 break;
148
149 case CHECK_CPULOAD: 107 case CHECK_CPULOAD:
150 108 if (config.value_list == NULL) {
151 if (value_list == NULL)
152 output_message = strdup(_("missing -l parameters")); 109 output_message = strdup(_("missing -l parameters"));
153 else if (!strtoularray(lvalue_list, value_list, ",")) 110 } else if (!strtoularray(lvalue_list, config.value_list, ",")) {
154 output_message = strdup(_("wrong -l parameter.")); 111 output_message = strdup(_("wrong -l parameter."));
155 else { 112 } else {
156 /* -l parameters is present with only integers */ 113 /* -l parameters is present with only integers */
157 return_code = STATE_OK; 114 return_code = STATE_OK;
158 temp_string = strdup(_("CPU Load")); 115 temp_string = strdup(_("CPU Load"));
159 temp_string_perf = strdup(" "); 116 temp_string_perf = strdup(" ");
160 117
161 /* loop until one of the parameters is wrong or not present */ 118 /* loop until one of the parameters is wrong or not present */
162 while (lvalue_list[0 + offset] > (unsigned long)0 && lvalue_list[0 + offset] <= (unsigned long)17280 && 119 int offset = 0;
163 lvalue_list[1 + offset] > (unsigned long)0 && lvalue_list[1 + offset] <= (unsigned long)100 && 120 while (lvalue_list[0 + offset] > (unsigned long)0 &&
164 lvalue_list[2 + offset] > (unsigned long)0 && lvalue_list[2 + offset] <= (unsigned long)100) { 121 lvalue_list[0 + offset] <= (unsigned long)17280 &&
122 lvalue_list[1 + offset] > (unsigned long)0 &&
123 lvalue_list[1 + offset] <= (unsigned long)100 &&
124 lvalue_list[2 + offset] > (unsigned long)0 &&
125 lvalue_list[2 + offset] <= (unsigned long)100) {
165 126
166 /* Send request and retrieve data */ 127 /* Send request and retrieve data */
167 xasprintf(&send_buffer, "%s&2&%lu", req_password, lvalue_list[0 + offset]); 128 xasprintf(&send_buffer, "%s&2&%lu", config.req_password, lvalue_list[0 + offset]);
168 fetch_data(server_address, server_port, send_buffer); 129 fetch_data(config.server_address, config.server_port, send_buffer);
169 130
170 utilization = strtoul(recv_buffer, NULL, 10); 131 unsigned long utilization = strtoul(recv_buffer, NULL, 10);
171 132
172 /* Check if any of the request is in a warning or critical state */ 133 /* Check if any of the request is in a warning or critical state */
173 if (utilization >= lvalue_list[2 + offset]) 134 if (utilization >= lvalue_list[2 + offset]) {
174 return_code = STATE_CRITICAL; 135 return_code = STATE_CRITICAL;
175 else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) 136 } else if (utilization >= lvalue_list[1 + offset] && return_code < STATE_WARNING) {
176 return_code = STATE_WARNING; 137 return_code = STATE_WARNING;
138 }
177 139
178 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization, lvalue_list[0 + offset]); 140 xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization,
141 lvalue_list[0 + offset]);
179 xasprintf(&temp_string, "%s%s", temp_string, output_message); 142 xasprintf(&temp_string, "%s%s", temp_string, output_message);
180 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), lvalue_list[0 + offset], utilization, 143 xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"),
181 lvalue_list[1 + offset], lvalue_list[2 + offset]); 144 lvalue_list[0 + offset], utilization, lvalue_list[1 + offset],
145 lvalue_list[2 + offset]);
182 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata); 146 xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata);
183 offset += 3; /* move across the array */ 147 offset += 3; /* move across the array */
184 } 148 }
@@ -186,82 +150,97 @@ int main(int argc, char **argv) {
186 if (strlen(temp_string) > 10) { /* we had at least one loop */ 150 if (strlen(temp_string) > 10) { /* we had at least one loop */
187 output_message = strdup(temp_string); 151 output_message = strdup(temp_string);
188 perfdata = temp_string_perf; 152 perfdata = temp_string_perf;
189 } else 153 } else {
190 output_message = strdup(_("not enough values for -l parameters")); 154 output_message = strdup(_("not enough values for -l parameters"));
155 }
191 } 156 }
192 break; 157 break;
193 158 case CHECK_UPTIME: {
194 case CHECK_UPTIME: 159 char *tmp_value_list = config.value_list;
195 160 if (config.value_list == NULL) {
196 if (value_list == NULL) { 161 tmp_value_list = "minutes";
197 value_list = "minutes";
198 } 162 }
199 if (strncmp(value_list, "seconds", strlen("seconds") + 1) && strncmp(value_list, "minutes", strlen("minutes") + 1) && 163 if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) &&
200 strncmp(value_list, "hours", strlen("hours") + 1) && strncmp(value_list, "days", strlen("days") + 1)) { 164 strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) &&
165 strncmp(config.value_list, "hours", strlen("hours") + 1) &&
166 strncmp(tmp_value_list, "days", strlen("days") + 1)) {
201 167
202 output_message = strdup(_("wrong -l argument")); 168 output_message = strdup(_("wrong -l argument"));
203 } else { 169 } else {
204 xasprintf(&send_buffer, "%s&3", req_password); 170 xasprintf(&send_buffer, "%s&3", config.req_password);
205 fetch_data(server_address, server_port, send_buffer); 171 fetch_data(config.server_address, config.server_port, send_buffer);
206 uptime = strtoul(recv_buffer, NULL, 10); 172 unsigned long uptime = strtoul(recv_buffer, NULL, 10);
207 updays = uptime / 86400; 173 int updays = uptime / 86400;
208 uphours = (uptime % 86400) / 3600; 174 int uphours = (uptime % 86400) / 3600;
209 upminutes = ((uptime % 86400) % 3600) / 60; 175 int upminutes = ((uptime % 86400) % 3600) / 60;
210 176
211 if (!strncmp(value_list, "minutes", strlen("minutes"))) 177 if (!strncmp(tmp_value_list, "minutes", strlen("minutes"))) {
212 uptime = uptime / 60; 178 uptime = uptime / 60;
213 else if (!strncmp(value_list, "hours", strlen("hours"))) 179 } else if (!strncmp(tmp_value_list, "hours", strlen("hours"))) {
214 uptime = uptime / 3600; 180 uptime = uptime / 3600;
215 else if (!strncmp(value_list, "days", strlen("days"))) 181 } else if (!strncmp(tmp_value_list, "days", strlen("days"))) {
216 uptime = uptime / 86400; 182 uptime = uptime / 86400;
183 }
217 /* else uptime in seconds, nothing to do */ 184 /* else uptime in seconds, nothing to do */
218 185
219 xasprintf(&output_message, _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays, uphours, upminutes, 186 xasprintf(&output_message,
220 uptime); 187 _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays,
188 uphours, upminutes, uptime);
221 189
222 if (check_critical_value && uptime <= critical_value) 190 if (config.check_critical_value && uptime <= config.critical_value) {
223 return_code = STATE_CRITICAL; 191 return_code = STATE_CRITICAL;
224 else if (check_warning_value && uptime <= warning_value) 192 } else if (config.check_warning_value && uptime <= config.warning_value) {
225 return_code = STATE_WARNING; 193 return_code = STATE_WARNING;
226 else 194 } else {
227 return_code = STATE_OK; 195 return_code = STATE_OK;
196 }
228 } 197 }
229 break; 198 } break;
230
231 case CHECK_USEDDISKSPACE: 199 case CHECK_USEDDISKSPACE:
232 200 if (config.value_list == NULL) {
233 if (value_list == NULL)
234 output_message = strdup(_("missing -l parameters")); 201 output_message = strdup(_("missing -l parameters"));
235 else if (strlen(value_list) != 1) 202 } else if (strlen(config.value_list) != 1) {
236 output_message = strdup(_("wrong -l argument")); 203 output_message = strdup(_("wrong -l argument"));
237 else { 204 } else {
238 xasprintf(&send_buffer, "%s&4&%s", req_password, value_list); 205 xasprintf(&send_buffer, "%s&4&%s", config.req_password, config.value_list);
239 fetch_data(server_address, server_port, send_buffer); 206 fetch_data(config.server_address, config.server_port, send_buffer);
240 fds = strtok(recv_buffer, "&"); 207 char *fds = strtok(recv_buffer, "&");
241 tds = strtok(NULL, "&"); 208 char *tds = strtok(NULL, "&");
242 if (fds != NULL) 209 double total_disk_space = 0;
210 double free_disk_space = 0;
211 if (fds != NULL) {
243 free_disk_space = atof(fds); 212 free_disk_space = atof(fds);
244 if (tds != NULL) 213 }
214 if (tds != NULL) {
245 total_disk_space = atof(tds); 215 total_disk_space = atof(tds);
216 }
246 217
247 if (total_disk_space > 0 && free_disk_space >= 0) { 218 if (total_disk_space > 0 && free_disk_space >= 0) {
248 percent_used_space = ((total_disk_space - free_disk_space) / total_disk_space) * 100; 219 double percent_used_space =
249 warning_used_space = ((float)warning_value / 100) * total_disk_space; 220 ((total_disk_space - free_disk_space) / total_disk_space) * 100;
250 critical_used_space = ((float)critical_value / 100) * total_disk_space; 221 double warning_used_space = ((float)config.warning_value / 100) * total_disk_space;
251 222 double critical_used_space =
252 xasprintf(&temp_string, _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"), value_list, 223 ((float)config.critical_value / 100) * total_disk_space;
253 total_disk_space / 1073741824, (total_disk_space - free_disk_space) / 1073741824, percent_used_space, 224
254 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100); 225 xasprintf(
255 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), value_list, 226 &temp_string,
256 (total_disk_space - free_disk_space) / 1073741824, warning_used_space / 1073741824, 227 _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"),
257 critical_used_space / 1073741824, total_disk_space / 1073741824); 228 config.value_list, total_disk_space / 1073741824,
258 229 (total_disk_space - free_disk_space) / 1073741824, percent_used_space,
259 if (check_critical_value && percent_used_space >= critical_value) 230 free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100);
231 xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"),
232 config.value_list, (total_disk_space - free_disk_space) / 1073741824,
233 warning_used_space / 1073741824, critical_used_space / 1073741824,
234 total_disk_space / 1073741824);
235
236 if (config.check_critical_value && percent_used_space >= config.critical_value) {
260 return_code = STATE_CRITICAL; 237 return_code = STATE_CRITICAL;
261 else if (check_warning_value && percent_used_space >= warning_value) 238 } else if (config.check_warning_value &&
239 percent_used_space >= config.warning_value) {
262 return_code = STATE_WARNING; 240 return_code = STATE_WARNING;
263 else 241 } else {
264 return_code = STATE_OK; 242 return_code = STATE_OK;
243 }
265 244
266 output_message = strdup(temp_string); 245 output_message = strdup(temp_string);
267 perfdata = temp_string_perf; 246 perfdata = temp_string_perf;
@@ -271,60 +250,64 @@ int main(int argc, char **argv) {
271 } 250 }
272 } 251 }
273 break; 252 break;
274
275 case CHECK_SERVICESTATE: 253 case CHECK_SERVICESTATE:
276 case CHECK_PROCSTATE: 254 case CHECK_PROCSTATE:
277 255 if (config.value_list == NULL) {
278 if (value_list == NULL)
279 output_message = strdup(_("No service/process specified")); 256 output_message = strdup(_("No service/process specified"));
280 else { 257 } else {
281 preparelist(value_list); /* replace , between services with & to send the request */ 258 preparelist(
282 xasprintf(&send_buffer, "%s&%u&%s&%s", req_password, (vars_to_check == CHECK_SERVICESTATE) ? 5 : 6, 259 config.value_list); /* replace , between services with & to send the request */
283 (show_all) ? "ShowAll" : "ShowFail", value_list); 260 xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password,
284 fetch_data(server_address, server_port, send_buffer); 261 (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6,
285 numstr = strtok(recv_buffer, "&"); 262 (config.show_all) ? "ShowAll" : "ShowFail", config.value_list);
286 if (numstr == NULL) 263 fetch_data(config.server_address, config.server_port, send_buffer);
264 char *numstr = strtok(recv_buffer, "&");
265 if (numstr == NULL) {
287 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 266 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
267 }
288 return_code = atoi(numstr); 268 return_code = atoi(numstr);
289 temp_string = strtok(NULL, "&"); 269 temp_string = strtok(NULL, "&");
290 output_message = strdup(temp_string); 270 output_message = strdup(temp_string);
291 } 271 }
292 break; 272 break;
293
294 case CHECK_MEMUSE: 273 case CHECK_MEMUSE:
295 274 xasprintf(&send_buffer, "%s&7", config.req_password);
296 xasprintf(&send_buffer, "%s&7", req_password); 275 fetch_data(config.server_address, config.server_port, send_buffer);
297 fetch_data(server_address, server_port, send_buffer); 276 char *numstr = strtok(recv_buffer, "&");
298 numstr = strtok(recv_buffer, "&"); 277 if (numstr == NULL) {
299 if (numstr == NULL)
300 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 278 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
301 mem_commitLimit = atof(numstr); 279 }
280 double mem_commitLimit = atof(numstr);
302 numstr = strtok(NULL, "&"); 281 numstr = strtok(NULL, "&");
303 if (numstr == NULL) 282 if (numstr == NULL) {
304 die(STATE_UNKNOWN, _("could not fetch information from server\n")); 283 die(STATE_UNKNOWN, _("could not fetch information from server\n"));
305 mem_commitByte = atof(numstr); 284 }
306 percent_used_space = (mem_commitByte / mem_commitLimit) * 100; 285 double mem_commitByte = atof(numstr);
307 warning_used_space = ((float)warning_value / 100) * mem_commitLimit; 286 double percent_used_space = (mem_commitByte / mem_commitLimit) * 100;
308 critical_used_space = ((float)critical_value / 100) * mem_commitLimit; 287 double warning_used_space = ((float)config.warning_value / 100) * mem_commitLimit;
288 double critical_used_space = ((float)config.critical_value / 100) * mem_commitLimit;
309 289
310 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here, 290 /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here,
311 which equals RAM + Pagefiles. */ 291 which equals RAM + Pagefiles. */
312 xasprintf(&output_message, _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"), 292 xasprintf(
313 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space, (mem_commitLimit - mem_commitByte) / 1048567, 293 &output_message,
314 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100); 294 _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"),
315 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), mem_commitByte / 1048567, warning_used_space / 1048567, 295 mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space,
296 (mem_commitLimit - mem_commitByte) / 1048567,
297 (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100);
298 xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"),
299 mem_commitByte / 1048567, warning_used_space / 1048567,
316 critical_used_space / 1048567, mem_commitLimit / 1048567); 300 critical_used_space / 1048567, mem_commitLimit / 1048567);
317 301
318 return_code = STATE_OK; 302 return_code = STATE_OK;
319 if (check_critical_value && percent_used_space >= critical_value) 303 if (config.check_critical_value && percent_used_space >= config.critical_value) {
320 return_code = STATE_CRITICAL; 304 return_code = STATE_CRITICAL;
321 else if (check_warning_value && percent_used_space >= warning_value) 305 } else if (config.check_warning_value && percent_used_space >= config.warning_value) {
322 return_code = STATE_WARNING; 306 return_code = STATE_WARNING;
307 }
323 308
324 break; 309 break;
325 310 case CHECK_COUNTER: {
326 case CHECK_COUNTER:
327
328 /* 311 /*
329 CHECK_COUNTER has been modified to provide extensive perfdata information. 312 CHECK_COUNTER has been modified to provide extensive perfdata information.
330 In order to do this, some modifications have been done to the code 313 In order to do this, some modifications have been done to the code
@@ -341,31 +324,38 @@ int main(int argc, char **argv) {
341 the counter unit - that is, the dimensions of the counter you're getting. Examples: 324 the counter unit - that is, the dimensions of the counter you're getting. Examples:
342 pages/s, packets transferred, etc. 325 pages/s, packets transferred, etc.
343 326
344 4) If you want, you may provide the minimum and maximum values to expect. They aren't mandatory, 327 4) If you want, you may provide the minimum and maximum values to expect. They aren't
345 but once specified they MUST have the same order of magnitude and units of -w and -c; otherwise. 328 mandatory, but once specified they MUST have the same order of magnitude and units of -w and
346 strange things will happen when you make graphs of your data. 329 -c; otherwise. strange things will happen when you make graphs of your data.
347 */ 330 */
348 331
349 if (value_list == NULL) 332 double counter_value = 0.0;
333 if (config.value_list == NULL) {
350 output_message = strdup(_("No counter specified")); 334 output_message = strdup(_("No counter specified"));
351 else { 335 } else {
352 preparelist(value_list); /* replace , between services with & to send the request */ 336 preparelist(
353 isPercent = (strchr(value_list, '%') != NULL); 337 config.value_list); /* replace , between services with & to send the request */
338 bool isPercent = (strchr(config.value_list, '%') != NULL);
354 339
355 strtok(value_list, "&"); /* burn the first parameters */ 340 strtok(config.value_list, "&"); /* burn the first parameters */
356 description = strtok(NULL, "&"); 341 description = strtok(NULL, "&");
357 counter_unit = strtok(NULL, "&"); 342 counter_unit = strtok(NULL, "&");
358 xasprintf(&send_buffer, "%s&8&%s", req_password, value_list); 343 xasprintf(&send_buffer, "%s&8&%s", config.req_password, config.value_list);
359 fetch_data(server_address, server_port, send_buffer); 344 fetch_data(config.server_address, config.server_port, send_buffer);
360 counter_value = atof(recv_buffer); 345 counter_value = atof(recv_buffer);
361 346
362 if (description == NULL) 347 bool allRight = false;
348 if (description == NULL) {
363 xasprintf(&output_message, "%.f", counter_value); 349 xasprintf(&output_message, "%.f", counter_value);
364 else if (isPercent) { 350 } else if (isPercent) {
365 counter_unit = strdup("%"); 351 counter_unit = strdup("%");
366 allRight = true; 352 allRight = true;
367 } 353 }
368 354
355 char *minval = NULL;
356 char *maxval = NULL;
357 double fminval = 0;
358 double fmaxval = 0;
369 if ((counter_unit != NULL) && (!allRight)) { 359 if ((counter_unit != NULL) && (!allRight)) {
370 minval = strtok(NULL, "&"); 360 minval = strtok(NULL, "&");
371 maxval = strtok(NULL, "&"); 361 maxval = strtok(NULL, "&");
@@ -375,84 +365,92 @@ int main(int argc, char **argv) {
375 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1; 365 fminval = (minval != NULL) ? strtod(minval, &errcvt) : -1;
376 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1; 366 fmaxval = (minval != NULL) ? strtod(maxval, &errcvt) : -1;
377 367
378 if ((fminval == 0) && (minval == errcvt)) 368 if ((fminval == 0) && (minval == errcvt)) {
379 output_message = strdup(_("Minimum value contains non-numbers")); 369 output_message = strdup(_("Minimum value contains non-numbers"));
380 else { 370 } else {
381 if ((fmaxval == 0) && (maxval == errcvt)) 371 if ((fmaxval == 0) && (maxval == errcvt)) {
382 output_message = strdup(_("Maximum value contains non-numbers")); 372 output_message = strdup(_("Maximum value contains non-numbers"));
383 else 373 } else {
384 allRight = true; /* Everything is OK. */ 374 allRight = true; /* Everything is OK. */
375 }
385 } 376 }
386 } else if ((counter_unit == NULL) && (description != NULL)) 377 } else if ((counter_unit == NULL) && (description != NULL)) {
387 output_message = strdup(_("No unit counter specified")); 378 output_message = strdup(_("No unit counter specified"));
379 }
388 380
389 if (allRight) { 381 if (allRight) {
390 /* Let's format the output string, finally... */ 382 /* Let's format the output string, finally... */
391 if (strstr(description, "%") == NULL) { 383 if (strstr(description, "%") == NULL) {
392 xasprintf(&output_message, "%s = %.2f %s", description, counter_value, counter_unit); 384 xasprintf(&output_message, "%s = %.2f %s", description, counter_value,
385 counter_unit);
393 } else { 386 } else {
394 /* has formatting, will segv if wrong */ 387 /* has formatting, will segv if wrong */
395 xasprintf(&output_message, description, counter_value); 388 xasprintf(&output_message, description, counter_value);
396 } 389 }
397 xasprintf(&output_message, "%s |", output_message); 390 xasprintf(&output_message, "%s |", output_message);
398 xasprintf(&output_message, "%s %s", output_message, 391 xasprintf(&output_message, "%s %s", output_message,
399 fperfdata(description, counter_value, counter_unit, 1, warning_value, 1, critical_value, 392 fperfdata(description, counter_value, counter_unit, 1,
400 (!(isPercent) && (minval != NULL)), fminval, (!(isPercent) && (minval != NULL)), fmaxval)); 393 config.warning_value, 1, config.critical_value,
394 (!(isPercent) && (minval != NULL)), fminval,
395 (!(isPercent) && (minval != NULL)), fmaxval));
401 } 396 }
402 } 397 }
403 398
404 if (critical_value > warning_value) { /* Normal thresholds */ 399 if (config.critical_value > config.warning_value) { /* Normal thresholds */
405 if (check_critical_value && counter_value >= critical_value) 400 if (config.check_critical_value && counter_value >= config.critical_value) {
406 return_code = STATE_CRITICAL; 401 return_code = STATE_CRITICAL;
407 else if (check_warning_value && counter_value >= warning_value) 402 } else if (config.check_warning_value && counter_value >= config.warning_value) {
408 return_code = STATE_WARNING; 403 return_code = STATE_WARNING;
409 else 404 } else {
410 return_code = STATE_OK; 405 return_code = STATE_OK;
406 }
411 } else { /* inverse thresholds */ 407 } else { /* inverse thresholds */
412 return_code = STATE_OK; 408 return_code = STATE_OK;
413 if (check_critical_value && counter_value <= critical_value) 409 if (config.check_critical_value && counter_value <= config.critical_value) {
414 return_code = STATE_CRITICAL; 410 return_code = STATE_CRITICAL;
415 else if (check_warning_value && counter_value <= warning_value) 411 } else if (config.check_warning_value && counter_value <= config.warning_value) {
416 return_code = STATE_WARNING; 412 return_code = STATE_WARNING;
413 }
417 } 414 }
418 break; 415 } break;
419
420 case CHECK_FILEAGE: 416 case CHECK_FILEAGE:
421 417 if (config.value_list == NULL) {
422 if (value_list == NULL)
423 output_message = strdup(_("No counter specified")); 418 output_message = strdup(_("No counter specified"));
424 else { 419 } else {
425 preparelist(value_list); /* replace , between services with & to send the request */ 420 preparelist(
426 xasprintf(&send_buffer, "%s&9&%s", req_password, value_list); 421 config.value_list); /* replace , between services with & to send the request */
427 fetch_data(server_address, server_port, send_buffer); 422 xasprintf(&send_buffer, "%s&9&%s", config.req_password, config.value_list);
428 age_in_minutes = atoi(strtok(recv_buffer, "&")); 423 fetch_data(config.server_address, config.server_port, send_buffer);
424 unsigned long age_in_minutes = atoi(strtok(recv_buffer, "&"));
429 description = strtok(NULL, "&"); 425 description = strtok(NULL, "&");
430 output_message = strdup(description); 426 output_message = strdup(description);
431 427
432 if (critical_value > warning_value) { /* Normal thresholds */ 428 if (config.critical_value > config.warning_value) { /* Normal thresholds */
433 if (check_critical_value && age_in_minutes >= critical_value) 429 if (config.check_critical_value && age_in_minutes >= config.critical_value) {
434 return_code = STATE_CRITICAL; 430 return_code = STATE_CRITICAL;
435 else if (check_warning_value && age_in_minutes >= warning_value) 431 } else if (config.check_warning_value && age_in_minutes >= config.warning_value) {
436 return_code = STATE_WARNING; 432 return_code = STATE_WARNING;
437 else 433 } else {
438 return_code = STATE_OK; 434 return_code = STATE_OK;
435 }
439 } else { /* inverse thresholds */ 436 } else { /* inverse thresholds */
440 if (check_critical_value && age_in_minutes <= critical_value) 437 if (config.check_critical_value && age_in_minutes <= config.critical_value) {
441 return_code = STATE_CRITICAL; 438 return_code = STATE_CRITICAL;
442 else if (check_warning_value && age_in_minutes <= warning_value) 439 } else if (config.check_warning_value && age_in_minutes <= config.warning_value) {
443 return_code = STATE_WARNING; 440 return_code = STATE_WARNING;
444 else 441 } else {
445 return_code = STATE_OK; 442 return_code = STATE_OK;
443 }
446 } 444 }
447 } 445 }
448 break; 446 break;
449 447
450 case CHECK_INSTANCES: 448 case CHECK_INSTANCES:
451 if (value_list == NULL) 449 if (config.value_list == NULL) {
452 output_message = strdup(_("No counter specified")); 450 output_message = strdup(_("No counter specified"));
453 else { 451 } else {
454 xasprintf(&send_buffer, "%s&10&%s", req_password, value_list); 452 xasprintf(&send_buffer, "%s&10&%s", config.req_password, config.value_list);
455 fetch_data(server_address, server_port, send_buffer); 453 fetch_data(config.server_address, config.server_port, send_buffer);
456 if (!strncmp(recv_buffer, "ERROR", 5)) { 454 if (!strncmp(recv_buffer, "ERROR", 5)) {
457 printf("NSClient - %s\n", recv_buffer); 455 printf("NSClient - %s\n", recv_buffer);
458 exit(STATE_UNKNOWN); 456 exit(STATE_UNKNOWN);
@@ -471,18 +469,16 @@ int main(int argc, char **argv) {
471 /* reset timeout */ 469 /* reset timeout */
472 alarm(0); 470 alarm(0);
473 471
474 if (perfdata == NULL) 472 if (perfdata == NULL) {
475 printf("%s\n", output_message); 473 printf("%s\n", output_message);
476 else 474 } else {
477 printf("%s | %s\n", output_message, perfdata); 475 printf("%s | %s\n", output_message, perfdata);
476 }
478 return return_code; 477 return return_code;
479} 478}
480 479
481/* process command-line arguments */ 480/* process command-line arguments */
482int process_arguments(int argc, char **argv) { 481check_nt_config_wrapper process_arguments(int argc, char **argv) {
483 int c;
484
485 int option = 0;
486 static struct option longopts[] = {{"port", required_argument, 0, 'p'}, 482 static struct option longopts[] = {{"port", required_argument, 0, 'p'},
487 {"timeout", required_argument, 0, 't'}, 483 {"timeout", required_argument, 0, 't'},
488 {"critical", required_argument, 0, 'c'}, 484 {"critical", required_argument, 0, 'c'},
@@ -497,34 +493,44 @@ int process_arguments(int argc, char **argv) {
497 {"help", no_argument, 0, 'h'}, 493 {"help", no_argument, 0, 'h'},
498 {0, 0, 0, 0}}; 494 {0, 0, 0, 0}};
499 495
496 check_nt_config_wrapper result = {
497 .errorcode = OK,
498 .config = check_nt_config_init(),
499 };
500
500 /* no options were supplied */ 501 /* no options were supplied */
501 if (argc < 2) 502 if (argc < 2) {
502 return ERROR; 503 result.errorcode = ERROR;
504 return result;
505 }
503 506
504 /* backwards compatibility */ 507 /* backwards compatibility */
505 if (!is_option(argv[1])) { 508 if (!is_option(argv[1])) {
506 server_address = strdup(argv[1]); 509 result.config.server_address = strdup(argv[1]);
507 argv[1] = argv[0]; 510 argv[1] = argv[0];
508 argv = &argv[1]; 511 argv = &argv[1];
509 argc--; 512 argc--;
510 } 513 }
511 514
512 for (c = 1; c < argc; c++) { 515 for (int index = 1; index < argc; index++) {
513 if (strcmp("-to", argv[c]) == 0) 516 if (strcmp("-to", argv[index]) == 0) {
514 strcpy(argv[c], "-t"); 517 strcpy(argv[index], "-t");
515 else if (strcmp("-wv", argv[c]) == 0) 518 } else if (strcmp("-wv", argv[index]) == 0) {
516 strcpy(argv[c], "-w"); 519 strcpy(argv[index], "-w");
517 else if (strcmp("-cv", argv[c]) == 0) 520 } else if (strcmp("-cv", argv[index]) == 0) {
518 strcpy(argv[c], "-c"); 521 strcpy(argv[index], "-c");
522 }
519 } 523 }
520 524
521 while (1) { 525 int option = 0;
522 c = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option); 526 while (true) {
527 int option_index = getopt_long(argc, argv, "+hVH:t:c:w:p:v:l:s:d:u", longopts, &option);
523 528
524 if (c == -1 || c == EOF || c == 1) 529 if (option_index == -1 || option_index == EOF || option_index == 1) {
525 break; 530 break;
531 }
526 532
527 switch (c) { 533 switch (option_index) {
528 case '?': /* print short usage statement if args not parsable */ 534 case '?': /* print short usage statement if args not parsable */
529 usage5(); 535 usage5();
530 case 'h': /* help */ 536 case 'h': /* help */
@@ -534,118 +540,128 @@ int process_arguments(int argc, char **argv) {
534 print_revision(progname, NP_VERSION); 540 print_revision(progname, NP_VERSION);
535 exit(STATE_UNKNOWN); 541 exit(STATE_UNKNOWN);
536 case 'H': /* hostname */ 542 case 'H': /* hostname */
537 server_address = optarg; 543 result.config.server_address = optarg;
538 break; 544 break;
539 case 's': /* password */ 545 case 's': /* password */
540 req_password = optarg; 546 result.config.req_password = optarg;
541 break; 547 break;
542 case 'p': /* port */ 548 case 'p': /* port */
543 if (is_intnonneg(optarg)) 549 if (is_intnonneg(optarg)) {
544 server_port = atoi(optarg); 550 result.config.server_port = atoi(optarg);
545 else 551 } else {
546 die(STATE_UNKNOWN, _("Server port must be an integer\n")); 552 die(STATE_UNKNOWN, _("Server port must be an integer\n"));
553 }
547 break; 554 break;
548 case 'v': 555 case 'v':
549 if (strlen(optarg) < 4) 556 if (strlen(optarg) < 4) {
550 return ERROR; 557 result.errorcode = ERROR;
551 if (!strcmp(optarg, "CLIENTVERSION")) 558 return result;
552 vars_to_check = CHECK_CLIENTVERSION; 559 }
553 else if (!strcmp(optarg, "CPULOAD")) 560 if (!strcmp(optarg, "CLIENTVERSION")) {
554 vars_to_check = CHECK_CPULOAD; 561 result.config.vars_to_check = CHECK_CLIENTVERSION;
555 else if (!strcmp(optarg, "UPTIME")) 562 } else if (!strcmp(optarg, "CPULOAD")) {
556 vars_to_check = CHECK_UPTIME; 563 result.config.vars_to_check = CHECK_CPULOAD;
557 else if (!strcmp(optarg, "USEDDISKSPACE")) 564 } else if (!strcmp(optarg, "UPTIME")) {
558 vars_to_check = CHECK_USEDDISKSPACE; 565 result.config.vars_to_check = CHECK_UPTIME;
559 else if (!strcmp(optarg, "SERVICESTATE")) 566 } else if (!strcmp(optarg, "USEDDISKSPACE")) {
560 vars_to_check = CHECK_SERVICESTATE; 567 result.config.vars_to_check = CHECK_USEDDISKSPACE;
561 else if (!strcmp(optarg, "PROCSTATE")) 568 } else if (!strcmp(optarg, "SERVICESTATE")) {
562 vars_to_check = CHECK_PROCSTATE; 569 result.config.vars_to_check = CHECK_SERVICESTATE;
563 else if (!strcmp(optarg, "MEMUSE")) 570 } else if (!strcmp(optarg, "PROCSTATE")) {
564 vars_to_check = CHECK_MEMUSE; 571 result.config.vars_to_check = CHECK_PROCSTATE;
565 else if (!strcmp(optarg, "COUNTER")) 572 } else if (!strcmp(optarg, "MEMUSE")) {
566 vars_to_check = CHECK_COUNTER; 573 result.config.vars_to_check = CHECK_MEMUSE;
567 else if (!strcmp(optarg, "FILEAGE")) 574 } else if (!strcmp(optarg, "COUNTER")) {
568 vars_to_check = CHECK_FILEAGE; 575 result.config.vars_to_check = CHECK_COUNTER;
569 else if (!strcmp(optarg, "INSTANCES")) 576 } else if (!strcmp(optarg, "FILEAGE")) {
570 vars_to_check = CHECK_INSTANCES; 577 result.config.vars_to_check = CHECK_FILEAGE;
571 else 578 } else if (!strcmp(optarg, "INSTANCES")) {
572 return ERROR; 579 result.config.vars_to_check = CHECK_INSTANCES;
580 } else {
581 result.errorcode = ERROR;
582 return result;
583 }
573 break; 584 break;
574 case 'l': /* value list */ 585 case 'l': /* value list */
575 value_list = optarg; 586 result.config.value_list = optarg;
576 break; 587 break;
577 case 'w': /* warning threshold */ 588 case 'w': /* warning threshold */
578 warning_value = strtoul(optarg, NULL, 10); 589 result.config.warning_value = strtoul(optarg, NULL, 10);
579 check_warning_value = true; 590 result.config.check_warning_value = true;
580 break; 591 break;
581 case 'c': /* critical threshold */ 592 case 'c': /* critical threshold */
582 critical_value = strtoul(optarg, NULL, 10); 593 result.config.critical_value = strtoul(optarg, NULL, 10);
583 check_critical_value = true; 594 result.config.check_critical_value = true;
584 break; 595 break;
585 case 'd': /* Display select for services */ 596 case 'd': /* Display select for services */
586 if (!strcmp(optarg, "SHOWALL")) 597 if (!strcmp(optarg, "SHOWALL")) {
587 show_all = true; 598 result.config.show_all = true;
599 }
588 break; 600 break;
589 case 'u': 601 case 'u':
590 socket_timeout_state = STATE_UNKNOWN; 602 socket_timeout_state = STATE_UNKNOWN;
591 break; 603 break;
592 case 't': /* timeout */ 604 case 't': /* timeout */
593 socket_timeout = atoi(optarg); 605 socket_timeout = atoi(optarg);
594 if (socket_timeout <= 0) 606 if (socket_timeout <= 0) {
595 return ERROR; 607 result.errorcode = ERROR;
608 return result;
609 }
596 } 610 }
597 } 611 }
598 if (server_address == NULL) 612 if (result.config.server_address == NULL) {
599 usage4(_("You must provide a server address or host name")); 613 usage4(_("You must provide a server address or host name"));
614 }
600 615
601 if (vars_to_check == CHECK_NONE) 616 if (result.config.vars_to_check == CHECK_NONE) {
602 return ERROR; 617 result.errorcode = ERROR;
618 return result;
619 }
603 620
604 if (req_password == NULL) 621 if (result.config.req_password == NULL) {
605 req_password = strdup(_("None")); 622 result.config.req_password = strdup(_("None"));
623 }
606 624
607 return OK; 625 return result;
608} 626}
609 627
610void fetch_data(const char *address, int port, const char *sendb) { 628void fetch_data(const char *address, int port, const char *sendb) {
611 int result; 629 int result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
612
613 result = process_tcp_request(address, port, sendb, recv_buffer, sizeof(recv_buffer));
614 630
615 if (result != STATE_OK) 631 if (result != STATE_OK) {
616 die(result, _("could not fetch information from server\n")); 632 die(result, _("could not fetch information from server\n"));
633 }
617 634
618 if (!strncmp(recv_buffer, "ERROR", 5)) 635 if (!strncmp(recv_buffer, "ERROR", 5)) {
619 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer); 636 die(STATE_UNKNOWN, "NSClient - %s\n", recv_buffer);
637 }
620} 638}
621 639
622bool strtoularray(unsigned long *array, char *string, const char *delim) { 640bool strtoularray(unsigned long *array, char *string, const char *delim) {
623 /* split a <delim> delimited string into a long array */ 641 /* split a <delim> delimited string into a long array */
624 int idx = 0; 642 for (int idx = 0; idx < MAX_VALUE_LIST; idx++) {
625 char *t1;
626
627 for (idx = 0; idx < MAX_VALUE_LIST; idx++)
628 array[idx] = 0; 643 array[idx] = 0;
644 }
629 645
630 idx = 0; 646 int idx = 0;
631 for (t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) { 647 for (char *t1 = strtok(string, delim); t1 != NULL; t1 = strtok(NULL, delim)) {
632 if (is_numeric(t1) && idx < MAX_VALUE_LIST) { 648 if (is_numeric(t1) && idx < MAX_VALUE_LIST) {
633 array[idx] = strtoul(t1, NULL, 10); 649 array[idx] = strtoul(t1, NULL, 10);
634 idx++; 650 idx++;
635 } else 651 } else {
636 return false; 652 return false;
653 }
637 } 654 }
638 return true; 655 return true;
639} 656}
640 657
641void preparelist(char *string) { 658void preparelist(char *string) {
642 /* Replace all , with & which is the delimiter for the request */ 659 /* Replace all , with & which is the delimiter for the request */
643 int i; 660 for (int i = 0; (size_t)i < strlen(string); i++) {
644
645 for (i = 0; (size_t)i < strlen(string); i++)
646 if (string[i] == ',') { 661 if (string[i] == ',') {
647 string[i] = '&'; 662 string[i] = '&';
648 } 663 }
664 }
649} 665}
650 666
651void print_help(void) { 667void print_help(void) {
@@ -735,25 +751,31 @@ void print_help(void) {
735 printf(" %s\n", "\"%%.f %%%% paging file used.\""); 751 printf(" %s\n", "\"%%.f %%%% paging file used.\"");
736 printf(" %s\n", "INSTANCES ="); 752 printf(" %s\n", "INSTANCES =");
737 printf(" %s\n", _("Check any performance counter object of Windows NT/2000.")); 753 printf(" %s\n", _("Check any performance counter object of Windows NT/2000."));
738 printf(" %s\n", _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>")); 754 printf(" %s\n",
755 _("Syntax: check_nt -H <hostname> -p <port> -v INSTANCES -l <counter object>"));
739 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),")); 756 printf(" %s\n", _("<counter object> is a Windows Perfmon Counter object (eg. Process),"));
740 printf(" %s\n", _("if it is two words, it should be enclosed in quotes")); 757 printf(" %s\n", _("if it is two words, it should be enclosed in quotes"));
741 printf(" %s\n", _("The returned results will be a comma-separated list of instances on ")); 758 printf(" %s\n", _("The returned results will be a comma-separated list of instances on "));
742 printf(" %s\n", _(" the selected computer for that object.")); 759 printf(" %s\n", _(" the selected computer for that object."));
743 printf(" %s\n", _("The purpose of this is to be run from command line to determine what instances")); 760 printf(" %s\n",
744 printf(" %s\n", _(" are available for monitoring without having to log onto the Windows server")); 761 _("The purpose of this is to be run from command line to determine what instances"));
762 printf(" %s\n",
763 _(" are available for monitoring without having to log onto the Windows server"));
745 printf(" %s\n", _(" to run Perfmon directly.")); 764 printf(" %s\n", _(" to run Perfmon directly."));
746 printf(" %s\n", _("It can also be used in scripts that automatically create the monitoring service")); 765 printf(" %s\n",
766 _("It can also be used in scripts that automatically create the monitoring service"));
747 printf(" %s\n", _(" configuration files.")); 767 printf(" %s\n", _(" configuration files."));
748 printf(" %s\n", _("Some examples:")); 768 printf(" %s\n", _("Some examples:"));
749 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process")); 769 printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process"));
750 770
751 printf("%s\n", _("Notes:")); 771 printf("%s\n", _("Notes:"));
752 printf(" %s\n", _("- The NSClient service should be running on the server to get any information")); 772 printf(" %s\n",
773 _("- The NSClient service should be running on the server to get any information"));
753 printf(" %s\n", "(http://nsclient.ready2run.nl)."); 774 printf(" %s\n", "(http://nsclient.ready2run.nl).");
754 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds")); 775 printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds"));
755 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error")); 776 printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error"));
756 printf(" %s\n", _("output when this happens contains \"Cannot map xxxxx to protocol number\".")); 777 printf(" %s\n",
778 _("output when this happens contains \"Cannot map xxxxx to protocol number\"."));
757 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt ")); 779 printf(" %s\n", _("One fix for this is to change the port to something else on check_nt "));
758 printf(" %s\n", _("and on the client service it\'s connecting to.")); 780 printf(" %s\n", _("and on the client service it\'s connecting to."));
759 781
diff --git a/plugins/check_nt.d/config.h b/plugins/check_nt.d/config.h
new file mode 100644
index 00000000..431889cb
--- /dev/null
+++ b/plugins/check_nt.d/config.h
@@ -0,0 +1,53 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PORT = 1248,
8};
9
10enum checkvars {
11 CHECK_NONE,
12 CHECK_CLIENTVERSION,
13 CHECK_CPULOAD,
14 CHECK_UPTIME,
15 CHECK_USEDDISKSPACE,
16 CHECK_SERVICESTATE,
17 CHECK_PROCSTATE,
18 CHECK_MEMUSE,
19 CHECK_COUNTER,
20 CHECK_FILEAGE,
21 CHECK_INSTANCES
22};
23
24typedef struct {
25 char *server_address;
26 int server_port;
27 char *req_password;
28 enum checkvars vars_to_check;
29 bool show_all;
30 char *value_list;
31 bool check_warning_value;
32 unsigned long warning_value;
33 bool check_critical_value;
34 unsigned long critical_value;
35} check_nt_config;
36
37check_nt_config check_nt_config_init() {
38 check_nt_config tmp = {
39 .server_address = NULL,
40 .server_port = PORT,
41 .req_password = NULL,
42
43 .vars_to_check = CHECK_NONE,
44 .show_all = false,
45 .value_list = NULL,
46
47 .check_warning_value = false,
48 .warning_value = 0,
49 .check_critical_value = false,
50 .critical_value = 0,
51 };
52 return tmp;
53}
diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c
index d33f8786..b22cc3c1 100644
--- a/plugins/check_ntp.c
+++ b/plugins/check_ntp.c
@@ -1,34 +1,34 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_ntp plugin 3 * Monitoring check_ntp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006 Sean Finney <seanius@seanius.net> 6 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
7* Copyright (c) 2006-2024 Monitoring Plugins Development Team 7 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains the check_ntp plugin 11 * This file contains the check_ntp plugin
12* 12 *
13* This plugin to check ntp servers independent of any commandline 13 * This plugin to check ntp servers independent of any commandline
14* programs or external libraries. 14 * programs or external libraries.
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30* 30 *
31*****************************************************************************/ 31 *****************************************************************************/
32 32
33const char *progname = "check_ntp"; 33const char *progname = "check_ntp";
34const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
@@ -38,24 +38,24 @@ const char *email = "devel@monitoring-plugins.org";
38#include "netutils.h" 38#include "netutils.h"
39#include "utils.h" 39#include "utils.h"
40 40
41static char *server_address=NULL; 41static char *server_address = NULL;
42static int verbose=0; 42static int verbose = 0;
43static bool do_offset = false; 43static bool do_offset = false;
44static char *owarn="60"; 44static char *owarn = "60";
45static char *ocrit="120"; 45static char *ocrit = "120";
46static bool do_jitter = false; 46static bool do_jitter = false;
47static char *jwarn="5000"; 47static char *jwarn = "5000";
48static char *jcrit="10000"; 48static char *jcrit = "10000";
49 49
50static int process_arguments (int /*argc*/, char ** /*argv*/); 50static int process_arguments(int /*argc*/, char ** /*argv*/);
51static thresholds *offset_thresholds = NULL; 51static thresholds *offset_thresholds = NULL;
52static thresholds *jitter_thresholds = NULL; 52static thresholds *jitter_thresholds = NULL;
53static void print_help (void); 53static void print_help(void);
54void print_usage (void); 54void print_usage(void);
55 55
56/* number of times to perform each request to get a good average. */ 56/* number of times to perform each request to get a good average. */
57#ifndef AVG_NUM 57#ifndef AVG_NUM
58#define AVG_NUM 4 58# define AVG_NUM 4
59#endif 59#endif
60 60
61/* max size of control message data */ 61/* max size of control message data */
@@ -63,17 +63,17 @@ void print_usage (void);
63 63
64/* this structure holds everything in an ntp request/response as per rfc1305 */ 64/* this structure holds everything in an ntp request/response as per rfc1305 */
65typedef struct { 65typedef struct {
66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 66 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
67 uint8_t stratum; /* clock stratum */ 67 uint8_t stratum; /* clock stratum */
68 int8_t poll; /* polling interval */ 68 int8_t poll; /* polling interval */
69 int8_t precision; /* precision of the local clock */ 69 int8_t precision; /* precision of the local clock */
70 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ 70 int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */
71 uint32_t rtdisp; /* like above, but for max err to primary src */ 71 uint32_t rtdisp; /* like above, but for max err to primary src */
72 uint32_t refid; /* ref clock identifier */ 72 uint32_t refid; /* ref clock identifier */
73 uint64_t refts; /* reference timestamp. local time local clock */ 73 uint64_t refts; /* reference timestamp. local time local clock */
74 uint64_t origts; /* time at which request departed client */ 74 uint64_t origts; /* time at which request departed client */
75 uint64_t rxts; /* time at which request arrived at server */ 75 uint64_t rxts; /* time at which request arrived at server */
76 uint64_t txts; /* time at which request departed server */ 76 uint64_t txts; /* time at which request departed server */
77} ntp_message; 77} ntp_message;
78 78
79/* this structure holds data about results from querying offset from a peer */ 79/* this structure holds data about results from querying offset from a peer */
@@ -84,20 +84,20 @@ typedef struct {
84 double rtdelay; /* converted from the ntp_message */ 84 double rtdelay; /* converted from the ntp_message */
85 double rtdisp; /* converted from the ntp_message */ 85 double rtdisp; /* converted from the ntp_message */
86 double offset[AVG_NUM]; /* offsets from each response */ 86 double offset[AVG_NUM]; /* offsets from each response */
87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 87 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
88} ntp_server_results; 88} ntp_server_results;
89 89
90/* this structure holds everything in an ntp control message as per rfc1305 */ 90/* this structure holds everything in an ntp control message as per rfc1305 */
91typedef struct { 91typedef struct {
92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 92 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
93 uint8_t op; /* R,E,M bits and Opcode */ 93 uint8_t op; /* R,E,M bits and Opcode */
94 uint16_t seq; /* Packet sequence */ 94 uint16_t seq; /* Packet sequence */
95 uint16_t status; /* Clock status */ 95 uint16_t status; /* Clock status */
96 uint16_t assoc; /* Association */ 96 uint16_t assoc; /* Association */
97 uint16_t offset; /* Similar to TCP sequence # */ 97 uint16_t offset; /* Similar to TCP sequence # */
98 uint16_t count; /* # bytes of data */ 98 uint16_t count; /* # bytes of data */
99 char data[MAX_CM_SIZE]; /* ASCII data of the request */ 99 char data[MAX_CM_SIZE]; /* ASCII data of the request */
100 /* NB: not necessarily NULL terminated! */ 100 /* NB: not necessarily NULL terminated! */
101} ntp_control_message; 101} ntp_control_message;
102 102
103/* this is an association/status-word pair found in control packet responses */ 103/* this is an association/status-word pair found in control packet responses */
@@ -108,38 +108,50 @@ typedef struct {
108 108
109/* bits 1,2 are the leap indicator */ 109/* bits 1,2 are the leap indicator */
110#define LI_MASK 0xc0 110#define LI_MASK 0xc0
111#define LI(x) ((x&LI_MASK)>>6) 111#define LI(x) ((x & LI_MASK) >> 6)
112#define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0) 112#define LI_SET(x, y) \
113 do { \
114 x |= ((y << 6) & LI_MASK); \
115 } while (0)
113/* and these are the values of the leap indicator */ 116/* and these are the values of the leap indicator */
114#define LI_NOWARNING 0x00 117#define LI_NOWARNING 0x00
115#define LI_EXTRASEC 0x01 118#define LI_EXTRASEC 0x01
116#define LI_MISSINGSEC 0x02 119#define LI_MISSINGSEC 0x02
117#define LI_ALARM 0x03 120#define LI_ALARM 0x03
118/* bits 3,4,5 are the ntp version */ 121/* bits 3,4,5 are the ntp version */
119#define VN_MASK 0x38 122#define VN_MASK 0x38
120#define VN(x) ((x&VN_MASK)>>3) 123#define VN(x) ((x & VN_MASK) >> 3)
121#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) 124#define VN_SET(x, y) \
125 do { \
126 x |= ((y << 3) & VN_MASK); \
127 } while (0)
122#define VN_RESERVED 0x02 128#define VN_RESERVED 0x02
123/* bits 6,7,8 are the ntp mode */ 129/* bits 6,7,8 are the ntp mode */
124#define MODE_MASK 0x07 130#define MODE_MASK 0x07
125#define MODE(x) (x&MODE_MASK) 131#define MODE(x) (x & MODE_MASK)
126#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) 132#define MODE_SET(x, y) \
133 do { \
134 x |= (y & MODE_MASK); \
135 } while (0)
127/* here are some values */ 136/* here are some values */
128#define MODE_CLIENT 0x03 137#define MODE_CLIENT 0x03
129#define MODE_CONTROLMSG 0x06 138#define MODE_CONTROLMSG 0x06
130/* In control message, bits 8-10 are R,E,M bits */ 139/* In control message, bits 8-10 are R,E,M bits */
131#define REM_MASK 0xe0 140#define REM_MASK 0xe0
132#define REM_RESP 0x80 141#define REM_RESP 0x80
133#define REM_ERROR 0x40 142#define REM_ERROR 0x40
134#define REM_MORE 0x20 143#define REM_MORE 0x20
135/* In control message, bits 11 - 15 are opcode */ 144/* In control message, bits 11 - 15 are opcode */
136#define OP_MASK 0x1f 145#define OP_MASK 0x1f
137#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) 146#define OP_SET(x, y) \
147 do { \
148 x |= (y & OP_MASK); \
149 } while (0)
138#define OP_READSTAT 0x01 150#define OP_READSTAT 0x01
139#define OP_READVAR 0x02 151#define OP_READVAR 0x02
140/* In peer status bytes, bits 6,7,8 determine clock selection status */ 152/* In peer status bytes, bits 6,7,8 determine clock selection status */
141#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) 153#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07)
142#define PEER_INCLUDED 0x04 154#define PEER_INCLUDED 0x04
143#define PEER_SYNCSOURCE 0x06 155#define PEER_SYNCSOURCE 0x06
144 156
145/** 157/**
@@ -153,82 +165,92 @@ typedef struct {
153 165
154/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" 166/* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
155 number. note that these can be used as lvalues too */ 167 number. note that these can be used as lvalues too */
156#define L16(x) (((uint16_t*)&x)[0]) 168#define L16(x) (((uint16_t *)&x)[0])
157#define R16(x) (((uint16_t*)&x)[1]) 169#define R16(x) (((uint16_t *)&x)[1])
158/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" 170/* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
159 number. these too can be used as lvalues */ 171 number. these too can be used as lvalues */
160#define L32(x) (((uint32_t*)&x)[0]) 172#define L32(x) (((uint32_t *)&x)[0])
161#define R32(x) (((uint32_t*)&x)[1]) 173#define R32(x) (((uint32_t *)&x)[1])
162 174
163/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ 175/* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */
164#define EPOCHDIFF 0x83aa7e80UL 176#define EPOCHDIFF 0x83aa7e80UL
165 177
166/* extract a 32-bit ntp fixed point number into a double */ 178/* extract a 32-bit ntp fixed point number into a double */
167#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0) 179#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0)
168 180
169/* likewise for a 64-bit ntp fp number */ 181/* likewise for a 64-bit ntp fp number */
170#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\ 182#define NTP64asDOUBLE(n) \
171 (ntohl(L32(n))-EPOCHDIFF) + \ 183 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
172 (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\ 184 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
173 0) 185 : 0)
174 186
175/* convert a struct timeval to a double */ 187/* convert a struct timeval to a double */
176#define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec)) 188#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
177 189
178/* convert an ntp 64-bit fp number to a struct timeval */ 190/* convert an ntp 64-bit fp number to a struct timeval */
179#define NTP64toTV(n,t) \ 191#define NTP64toTV(n, t) \
180 do{ if(!n) t.tv_sec = t.tv_usec = 0; \ 192 do { \
181 else { \ 193 if (!n) \
182 t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ 194 t.tv_sec = t.tv_usec = 0; \
183 t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ 195 else { \
184 } \ 196 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
185 }while(0) 197 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
198 } \
199 } while (0)
186 200
187/* convert a struct timeval to an ntp 64-bit fp number */ 201/* convert a struct timeval to an ntp 64-bit fp number */
188#define TVtoNTP64(t,n) \ 202#define TVtoNTP64(t, n) \
189 do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ 203 do { \
190 else { \ 204 if (!t.tv_usec && !t.tv_sec) \
191 L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ 205 n = 0x0UL; \
192 R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ 206 else { \
193 } \ 207 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
194 } while(0) 208 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
209 } \
210 } while (0)
195 211
196/* NTP control message header is 12 bytes, plus any data in the data 212/* NTP control message header is 12 bytes, plus any data in the data
197 * field, plus null padding to the nearest 32-bit boundary per rfc. 213 * field, plus null padding to the nearest 32-bit boundary per rfc.
198 */ 214 */
199#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0)) 215#define SIZEOF_NTPCM(m) \
216 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
200 217
201/* finally, a little helper or two for debugging: */ 218/* finally, a little helper or two for debugging: */
202#define DBG(x) do{if(verbose>1){ x; }}while(0); 219#define DBG(x) \
203#define PRINTSOCKADDR(x) \ 220 do { \
204 do{ \ 221 if (verbose > 1) { \
205 printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ 222 x; \
206 }while(0); 223 } \
224 } while (0);
225#define PRINTSOCKADDR(x) \
226 do { \
227 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
228 } while (0);
207 229
208/* calculate the offset of the local clock */ 230/* calculate the offset of the local clock */
209static inline double calc_offset(const ntp_message *m, const struct timeval *t){ 231static inline double calc_offset(const ntp_message *m, const struct timeval *t) {
210 double client_tx, peer_rx, peer_tx, client_rx; 232 double client_tx, peer_rx, peer_tx, client_rx;
211 client_tx = NTP64asDOUBLE(m->origts); 233 client_tx = NTP64asDOUBLE(m->origts);
212 peer_rx = NTP64asDOUBLE(m->rxts); 234 peer_rx = NTP64asDOUBLE(m->rxts);
213 peer_tx = NTP64asDOUBLE(m->txts); 235 peer_tx = NTP64asDOUBLE(m->txts);
214 client_rx=TVasDOUBLE((*t)); 236 client_rx = TVasDOUBLE((*t));
215 return (.5*((peer_tx-client_rx)+(peer_rx-client_tx))); 237 return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx)));
216} 238}
217 239
218/* print out a ntp packet in human readable/debuggable format */ 240/* print out a ntp packet in human readable/debuggable format */
219void print_ntp_message(const ntp_message *p){ 241void print_ntp_message(const ntp_message *p) {
220 struct timeval ref, orig, rx, tx; 242 struct timeval ref, orig, rx, tx;
221 243
222 NTP64toTV(p->refts,ref); 244 NTP64toTV(p->refts, ref);
223 NTP64toTV(p->origts,orig); 245 NTP64toTV(p->origts, orig);
224 NTP64toTV(p->rxts,rx); 246 NTP64toTV(p->rxts, rx);
225 NTP64toTV(p->txts,tx); 247 NTP64toTV(p->txts, tx);
226 248
227 printf("packet contents:\n"); 249 printf("packet contents:\n");
228 printf("\tflags: 0x%.2x\n", p->flags); 250 printf("\tflags: 0x%.2x\n", p->flags);
229 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); 251 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK);
230 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); 252 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK);
231 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); 253 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
232 printf("\tstratum = %d\n", p->stratum); 254 printf("\tstratum = %d\n", p->stratum);
233 printf("\tpoll = %g\n", pow(2, p->poll)); 255 printf("\tpoll = %g\n", pow(2, p->poll));
234 printf("\tprecision = %g\n", pow(2, p->precision)); 256 printf("\tprecision = %g\n", pow(2, p->precision));
@@ -241,32 +263,31 @@ void print_ntp_message(const ntp_message *p){
241 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 263 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
242} 264}
243 265
244void print_ntp_control_message(const ntp_control_message *p){ 266void print_ntp_control_message(const ntp_control_message *p) {
245 int i=0, numpeers=0; 267 int i = 0, numpeers = 0;
246 const ntp_assoc_status_pair *peer=NULL; 268 const ntp_assoc_status_pair *peer = NULL;
247 269
248 printf("control packet contents:\n"); 270 printf("control packet contents:\n");
249 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 271 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op);
250 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); 272 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK);
251 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); 273 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK);
252 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); 274 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK);
253 printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP); 275 printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP);
254 printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE); 276 printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE);
255 printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR); 277 printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR);
256 printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK); 278 printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK);
257 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); 279 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq));
258 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); 280 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status));
259 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); 281 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc));
260 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); 282 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset));
261 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); 283 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
262 numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); 284 numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair));
263 if(p->op&REM_RESP && p->op&OP_READSTAT){ 285 if (p->op & REM_RESP && p->op & OP_READSTAT) {
264 peer=(ntp_assoc_status_pair*)p->data; 286 peer = (ntp_assoc_status_pair *)p->data;
265 for(i=0;i<numpeers;i++){ 287 for (i = 0; i < numpeers; i++) {
266 printf("\tpeer id %.2x status %.2x", 288 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
267 ntohs(peer[i].assoc), ntohs(peer[i].status)); 289 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) {
268 if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){ 290 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
269 if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){
270 printf(" <-- current sync source"); 291 printf(" <-- current sync source");
271 } else { 292 } else {
272 printf(" <-- current sync candidate"); 293 printf(" <-- current sync candidate");
@@ -277,41 +298,45 @@ void print_ntp_control_message(const ntp_control_message *p){
277 } 298 }
278} 299}
279 300
280void setup_request(ntp_message *p){ 301void setup_request(ntp_message *p) {
281 struct timeval t; 302 struct timeval t;
282 303
283 memset(p, 0, sizeof(ntp_message)); 304 memset(p, 0, sizeof(ntp_message));
284 LI_SET(p->flags, LI_ALARM); 305 LI_SET(p->flags, LI_ALARM);
285 VN_SET(p->flags, 4); 306 VN_SET(p->flags, 4);
286 MODE_SET(p->flags, MODE_CLIENT); 307 MODE_SET(p->flags, MODE_CLIENT);
287 p->poll=4; 308 p->poll = 4;
288 p->precision=(int8_t)0xfa; 309 p->precision = (int8_t)0xfa;
289 L16(p->rtdelay)=htons(1); 310 L16(p->rtdelay) = htons(1);
290 L16(p->rtdisp)=htons(1); 311 L16(p->rtdisp) = htons(1);
291 312
292 gettimeofday(&t, NULL); 313 gettimeofday(&t, NULL);
293 TVtoNTP64(t,p->txts); 314 TVtoNTP64(t, p->txts);
294} 315}
295 316
296/* select the "best" server from a list of servers, and return its index. 317/* select the "best" server from a list of servers, and return its index.
297 * this is done by filtering servers based on stratum, dispersion, and 318 * this is done by filtering servers based on stratum, dispersion, and
298 * finally round-trip delay. */ 319 * finally round-trip delay. */
299int best_offset_server(const ntp_server_results *slist, int nservers){ 320int best_offset_server(const ntp_server_results *slist, int nservers) {
300 int cserver=0, best_server=-1; 321 int cserver = 0, best_server = -1;
301 322
302 /* for each server */ 323 /* for each server */
303 for(cserver=0; cserver<nservers; cserver++){ 324 for (cserver = 0; cserver < nservers; cserver++) {
304 /* We don't want any servers that fails these tests */ 325 /* We don't want any servers that fails these tests */
305 /* Sort out servers that didn't respond or responede with a 0 stratum; 326 /* Sort out servers that didn't respond or responede with a 0 stratum;
306 * stratum 0 is for reference clocks so no NTP server should ever report 327 * stratum 0 is for reference clocks so no NTP server should ever report
307 * a stratum 0 */ 328 * a stratum 0 */
308 if ( slist[cserver].stratum == 0){ 329 if (slist[cserver].stratum == 0) {
309 if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); 330 if (verbose) {
331 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
332 }
310 continue; 333 continue;
311 } 334 }
312 /* Sort out servers with error flags */ 335 /* Sort out servers with error flags */
313 if ( LI(slist[cserver].flags) == LI_ALARM ){ 336 if (LI(slist[cserver].flags) == LI_ALARM) {
314 if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); 337 if (verbose) {
338 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
339 }
315 continue; 340 continue;
316 } 341 }
317 342
@@ -325,13 +350,13 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
325 /* compare the server to the best one we've seen so far */ 350 /* compare the server to the best one we've seen so far */
326 /* does it have an equal or better stratum? */ 351 /* does it have an equal or better stratum? */
327 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 352 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
328 if(slist[cserver].stratum <= slist[best_server].stratum){ 353 if (slist[cserver].stratum <= slist[best_server].stratum) {
329 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 354 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
330 /* does it have an equal or better dispersion? */ 355 /* does it have an equal or better dispersion? */
331 if(slist[cserver].rtdisp <= slist[best_server].rtdisp){ 356 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) {
332 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 357 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
333 /* does it have a better rtdelay? */ 358 /* does it have a better rtdelay? */
334 if(slist[cserver].rtdelay < slist[best_server].rtdelay){ 359 if (slist[cserver].rtdelay < slist[best_server].rtdelay) {
335 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 360 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
336 best_server = cserver; 361 best_server = cserver;
337 DBG(printf("peer %d is now our best candidate\n", best_server)); 362 DBG(printf("peer %d is now our best candidate\n", best_server));
@@ -340,7 +365,7 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
340 } 365 }
341 } 366 }
342 367
343 if(best_server >= 0) { 368 if (best_server >= 0) {
344 DBG(printf("best server selected: peer %d\n", best_server)); 369 DBG(printf("best server selected: peer %d\n", best_server));
345 return best_server; 370 return best_server;
346 } else { 371 } else {
@@ -354,16 +379,16 @@ int best_offset_server(const ntp_server_results *slist, int nservers){
354 * we don't waste time sitting around waiting for single packets. 379 * we don't waste time sitting around waiting for single packets.
355 * - we also "manually" handle resolving host names and connecting, because 380 * - we also "manually" handle resolving host names and connecting, because
356 * we have to do it in a way that our lazy macros don't handle currently :( */ 381 * we have to do it in a way that our lazy macros don't handle currently :( */
357double offset_request(const char *host, int *status){ 382double offset_request(const char *host, int *status) {
358 int i=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0; 383 int i = 0, ga_result = 0, num_hosts = 0, *socklist = NULL, respnum = 0;
359 int servers_completed=0, one_read=0, servers_readable=0, best_index=-1; 384 int servers_completed = 0, one_read = 0, servers_readable = 0, best_index = -1;
360 time_t now_time=0, start_ts=0; 385 time_t now_time = 0, start_ts = 0;
361 ntp_message *req=NULL; 386 ntp_message *req = NULL;
362 double avg_offset=0.; 387 double avg_offset = 0.;
363 struct timeval recv_time; 388 struct timeval recv_time;
364 struct addrinfo *ai=NULL, *ai_tmp=NULL, hints; 389 struct addrinfo *ai = NULL, *ai_tmp = NULL, hints;
365 struct pollfd *ufds=NULL; 390 struct pollfd *ufds = NULL;
366 ntp_server_results *servers=NULL; 391 ntp_server_results *servers = NULL;
367 392
368 /* setup hints to only return results from getaddrinfo that we'd like */ 393 /* setup hints to only return results from getaddrinfo that we'd like */
369 memset(&hints, 0, sizeof(struct addrinfo)); 394 memset(&hints, 0, sizeof(struct addrinfo));
@@ -373,97 +398,112 @@ double offset_request(const char *host, int *status){
373 398
374 /* fill in ai with the list of hosts resolved by the host name */ 399 /* fill in ai with the list of hosts resolved by the host name */
375 ga_result = getaddrinfo(host, "123", &hints, &ai); 400 ga_result = getaddrinfo(host, "123", &hints, &ai);
376 if(ga_result!=0){ 401 if (ga_result != 0) {
377 die(STATE_UNKNOWN, "error getting address for %s: %s\n", 402 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
378 host, gai_strerror(ga_result));
379 } 403 }
380 404
381 /* count the number of returned hosts, and allocate stuff accordingly */ 405 /* count the number of returned hosts, and allocate stuff accordingly */
382 for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; } 406 for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
383 req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); 407 num_hosts++;
384 if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); 408 }
385 socklist=(int*)malloc(sizeof(int)*num_hosts); 409 req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
386 if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 410 if (req == NULL) {
387 ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); 411 die(STATE_UNKNOWN, "can not allocate ntp message array");
388 if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); 412 }
389 servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); 413 socklist = (int *)malloc(sizeof(int) * num_hosts);
390 if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); 414 if (socklist == NULL) {
391 memset(servers, 0, sizeof(ntp_server_results)*num_hosts); 415 die(STATE_UNKNOWN, "can not allocate socket array");
416 }
417 ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
418 if (ufds == NULL) {
419 die(STATE_UNKNOWN, "can not allocate socket array");
420 }
421 servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
422 if (servers == NULL) {
423 die(STATE_UNKNOWN, "can not allocate server array");
424 }
425 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
392 DBG(printf("Found %d peers to check\n", num_hosts)); 426 DBG(printf("Found %d peers to check\n", num_hosts));
393 427
394 /* setup each socket for writing, and the corresponding struct pollfd */ 428 /* setup each socket for writing, and the corresponding struct pollfd */
395 ai_tmp=ai; 429 ai_tmp = ai;
396 for(i=0;ai_tmp;i++){ 430 for (i = 0; ai_tmp; i++) {
397 socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); 431 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
398 if(socklist[i] == -1) { 432 if (socklist[i] == -1) {
399 perror(NULL); 433 perror(NULL);
400 die(STATE_UNKNOWN, "can not create new socket"); 434 die(STATE_UNKNOWN, "can not create new socket");
401 } 435 }
402 if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ 436 if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) {
403 /* don't die here, because it is enough if there is one server 437 /* don't die here, because it is enough if there is one server
404 answering in time. This also would break for dual ipv4/6 stacked 438 answering in time. This also would break for dual ipv4/6 stacked
405 ntp servers when the client only supports on of them. 439 ntp servers when the client only supports on of them.
406 */ 440 */
407 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); 441 DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno)));
408 } else { 442 } else {
409 ufds[i].fd=socklist[i]; 443 ufds[i].fd = socklist[i];
410 ufds[i].events=POLLIN; 444 ufds[i].events = POLLIN;
411 ufds[i].revents=0; 445 ufds[i].revents = 0;
412 } 446 }
413 ai_tmp = ai_tmp->ai_next; 447 ai_tmp = ai_tmp->ai_next;
414 } 448 }
415 449
416 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds 450 /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds
417 * have passed in order to ensure post-processing and jitter time. */ 451 * have passed in order to ensure post-processing and jitter time. */
418 now_time=start_ts=time(NULL); 452 now_time = start_ts = time(NULL);
419 while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){ 453 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
420 /* loop through each server and find each one which hasn't 454 /* loop through each server and find each one which hasn't
421 * been touched in the past second or so and is still lacking 455 * been touched in the past second or so and is still lacking
422 * some responses. for each of these servers, send a new request, 456 * some responses. for each of these servers, send a new request,
423 * and update the "waiting" timestamp with the current time. */ 457 * and update the "waiting" timestamp with the current time. */
424 now_time=time(NULL); 458 now_time = time(NULL);
425 459
426 for(i=0; i<num_hosts; i++){ 460 for (i = 0; i < num_hosts; i++) {
427 if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){ 461 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
428 if(verbose && servers[i].waiting != 0) printf("re-"); 462 if (verbose && servers[i].waiting != 0) {
429 if(verbose) printf("sending request to peer %d\n", i); 463 printf("re-");
464 }
465 if (verbose) {
466 printf("sending request to peer %d\n", i);
467 }
430 setup_request(&req[i]); 468 setup_request(&req[i]);
431 write(socklist[i], &req[i], sizeof(ntp_message)); 469 write(socklist[i], &req[i], sizeof(ntp_message));
432 servers[i].waiting=now_time; 470 servers[i].waiting = now_time;
433 break; 471 break;
434 } 472 }
435 } 473 }
436 474
437 /* quickly poll for any sockets with pending data */ 475 /* quickly poll for any sockets with pending data */
438 servers_readable=poll(ufds, num_hosts, 100); 476 servers_readable = poll(ufds, num_hosts, 100);
439 if(servers_readable==-1){ 477 if (servers_readable == -1) {
440 perror("polling ntp sockets"); 478 perror("polling ntp sockets");
441 die(STATE_UNKNOWN, "communication errors"); 479 die(STATE_UNKNOWN, "communication errors");
442 } 480 }
443 481
444 /* read from any sockets with pending data */ 482 /* read from any sockets with pending data */
445 for(i=0; servers_readable && i<num_hosts; i++){ 483 for (i = 0; servers_readable && i < num_hosts; i++) {
446 if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){ 484 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
447 if(verbose) { 485 if (verbose) {
448 printf("response from peer %d: ", i); 486 printf("response from peer %d: ", i);
449 } 487 }
450 488
451 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 489 read(ufds[i].fd, &req[i], sizeof(ntp_message));
452 gettimeofday(&recv_time, NULL); 490 gettimeofday(&recv_time, NULL);
453 DBG(print_ntp_message(&req[i])); 491 DBG(print_ntp_message(&req[i]));
454 respnum=servers[i].num_responses++; 492 respnum = servers[i].num_responses++;
455 servers[i].offset[respnum]=calc_offset(&req[i], &recv_time); 493 servers[i].offset[respnum] = calc_offset(&req[i], &recv_time);
456 if(verbose) { 494 if (verbose) {
457 printf("offset %.10g\n", servers[i].offset[respnum]); 495 printf("offset %.10g\n", servers[i].offset[respnum]);
458 } 496 }
459 servers[i].stratum=req[i].stratum; 497 servers[i].stratum = req[i].stratum;
460 servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp); 498 servers[i].rtdisp = NTP32asDOUBLE(req[i].rtdisp);
461 servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay); 499 servers[i].rtdelay = NTP32asDOUBLE(req[i].rtdelay);
462 servers[i].waiting=0; 500 servers[i].waiting = 0;
463 servers[i].flags=req[i].flags; 501 servers[i].flags = req[i].flags;
464 servers_readable--; 502 servers_readable--;
465 one_read = 1; 503 one_read = 1;
466 if(servers[i].num_responses==AVG_NUM) servers_completed++; 504 if (servers[i].num_responses == AVG_NUM) {
505 servers_completed++;
506 }
467 } 507 }
468 } 508 }
469 /* lather, rinse, repeat. */ 509 /* lather, rinse, repeat. */
@@ -474,15 +514,15 @@ double offset_request(const char *host, int *status){
474 } 514 }
475 515
476 /* now, pick the best server from the list */ 516 /* now, pick the best server from the list */
477 best_index=best_offset_server(servers, num_hosts); 517 best_index = best_offset_server(servers, num_hosts);
478 if(best_index < 0){ 518 if (best_index < 0) {
479 *status=STATE_UNKNOWN; 519 *status = STATE_UNKNOWN;
480 } else { 520 } else {
481 /* finally, calculate the average offset */ 521 /* finally, calculate the average offset */
482 for(i=0; i<servers[best_index].num_responses;i++){ 522 for (i = 0; i < servers[best_index].num_responses; i++) {
483 avg_offset+=servers[best_index].offset[i]; 523 avg_offset += servers[best_index].offset[i];
484 } 524 }
485 avg_offset/=servers[best_index].num_responses; 525 avg_offset /= servers[best_index].num_responses;
486 } 526 }
487 527
488 /* cleanup */ 528 /* cleanup */
@@ -496,12 +536,13 @@ double offset_request(const char *host, int *status){
496 free(req); 536 free(req);
497 freeaddrinfo(ai); 537 freeaddrinfo(ai);
498 538
499 if(verbose) printf("overall average offset: %.10g\n", avg_offset); 539 if (verbose) {
540 printf("overall average offset: %.10g\n", avg_offset);
541 }
500 return avg_offset; 542 return avg_offset;
501} 543}
502 544
503void 545void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) {
504setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
505 memset(p, 0, sizeof(ntp_control_message)); 546 memset(p, 0, sizeof(ntp_control_message));
506 LI_SET(p->flags, LI_NOWARNING); 547 LI_SET(p->flags, LI_NOWARNING);
507 VN_SET(p->flags, VN_RESERVED); 548 VN_SET(p->flags, VN_RESERVED);
@@ -512,16 +553,16 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
512} 553}
513 554
514/* XXX handle responses with the error bit set */ 555/* XXX handle responses with the error bit set */
515double jitter_request(int *status){ 556double jitter_request(int *status) {
516 int conn=-1, i, npeers=0, num_candidates=0; 557 int conn = -1, i, npeers = 0, num_candidates = 0;
517 bool syncsource_found = false; 558 bool syncsource_found = false;
518 int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0; 559 int run = 0, min_peer_sel = PEER_INCLUDED, num_selected = 0, num_valid = 0;
519 int peers_size=0, peer_offset=0; 560 int peers_size = 0, peer_offset = 0;
520 ntp_assoc_status_pair *peers=NULL; 561 ntp_assoc_status_pair *peers = NULL;
521 ntp_control_message req; 562 ntp_control_message req;
522 const char *getvar = "jitter"; 563 const char *getvar = "jitter";
523 double rval = 0.0, jitter = -1.0; 564 double rval = 0.0, jitter = -1.0;
524 char *startofvalue=NULL, *nptr=NULL; 565 char *startofvalue = NULL, *nptr = NULL;
525 void *tmp; 566 void *tmp;
526 567
527 /* Long-winded explanation: 568 /* Long-winded explanation:
@@ -542,54 +583,62 @@ double jitter_request(int *status){
542 583
543 /* keep sending requests until the server stops setting the 584 /* keep sending requests until the server stops setting the
544 * REM_MORE bit, though usually this is only 1 packet. */ 585 * REM_MORE bit, though usually this is only 1 packet. */
545 do{ 586 do {
546 setup_control_request(&req, OP_READSTAT, 1); 587 setup_control_request(&req, OP_READSTAT, 1);
547 DBG(printf("sending READSTAT request")); 588 DBG(printf("sending READSTAT request"));
548 write(conn, &req, SIZEOF_NTPCM(req)); 589 write(conn, &req, SIZEOF_NTPCM(req));
549 DBG(print_ntp_control_message(&req)); 590 DBG(print_ntp_control_message(&req));
550 /* Attempt to read the largest size packet possible */ 591 /* Attempt to read the largest size packet possible */
551 req.count=htons(MAX_CM_SIZE); 592 req.count = htons(MAX_CM_SIZE);
552 DBG(printf("receiving READSTAT response")) 593 DBG(printf("receiving READSTAT response"))
553 read(conn, &req, SIZEOF_NTPCM(req)); 594 read(conn, &req, SIZEOF_NTPCM(req));
554 DBG(print_ntp_control_message(&req)); 595 DBG(print_ntp_control_message(&req));
555 /* Each peer identifier is 4 bytes in the data section, which 596 /* Each peer identifier is 4 bytes in the data section, which
556 * we represent as a ntp_assoc_status_pair datatype. 597 * we represent as a ntp_assoc_status_pair datatype.
557 */ 598 */
558 peers_size+=ntohs(req.count); 599 peers_size += ntohs(req.count);
559 if((tmp=realloc(peers, peers_size)) == NULL) 600 if ((tmp = realloc(peers, peers_size)) == NULL) {
560 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 601 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
561 peers=tmp; 602 }
562 memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count)); 603 peers = tmp;
563 npeers=peers_size/sizeof(ntp_assoc_status_pair); 604 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count));
564 peer_offset+=ntohs(req.count); 605 npeers = peers_size / sizeof(ntp_assoc_status_pair);
565 } while(req.op&REM_MORE); 606 peer_offset += ntohs(req.count);
607 } while (req.op & REM_MORE);
566 608
567 /* first, let's find out if we have a sync source, or if there are 609 /* first, let's find out if we have a sync source, or if there are
568 * at least some candidates. in the case of the latter we'll issue 610 * at least some candidates. in the case of the latter we'll issue
569 * a warning but go ahead with the check on them. */ 611 * a warning but go ahead with the check on them. */
570 for (i = 0; i < npeers; i++){ 612 for (i = 0; i < npeers; i++) {
571 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){ 613 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
572 num_candidates++; 614 num_candidates++;
573 if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ 615 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
574 syncsource_found = true; 616 syncsource_found = true;
575 min_peer_sel=PEER_SYNCSOURCE; 617 min_peer_sel = PEER_SYNCSOURCE;
576 } 618 }
577 } 619 }
578 } 620 }
579 if(verbose) printf("%d candidate peers available\n", num_candidates); 621 if (verbose) {
580 if(verbose && syncsource_found) printf("synchronization source found\n"); 622 printf("%d candidate peers available\n", num_candidates);
581 if(! syncsource_found){ 623 }
624 if (verbose && syncsource_found) {
625 printf("synchronization source found\n");
626 }
627 if (!syncsource_found) {
582 *status = STATE_UNKNOWN; 628 *status = STATE_UNKNOWN;
583 if(verbose) printf("warning: no synchronization source found\n"); 629 if (verbose) {
630 printf("warning: no synchronization source found\n");
631 }
584 } 632 }
585 633
586 634 for (run = 0; run < AVG_NUM; run++) {
587 for (run=0; run<AVG_NUM; run++){ 635 if (verbose) {
588 if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM); 636 printf("jitter run %d of %d\n", run + 1, AVG_NUM);
589 for (i = 0; i < npeers; i++){ 637 }
638 for (i = 0; i < npeers; i++) {
590 /* Only query this server if it is the current sync source */ 639 /* Only query this server if it is the current sync source */
591 if (PEER_SEL(peers[i].status) >= min_peer_sel){ 640 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
592 char jitter_data[MAX_CM_SIZE+1]; 641 char jitter_data[MAX_CM_SIZE + 1];
593 size_t jitter_data_count; 642 size_t jitter_data_count;
594 643
595 num_selected++; 644 num_selected++;
@@ -602,7 +651,7 @@ double jitter_request(int *status){
602 */ 651 */
603 /* Older servers doesn't know what jitter is, so if we get an 652 /* Older servers doesn't know what jitter is, so if we get an
604 * error on the first pass we redo it with "dispersion" */ 653 * error on the first pass we redo it with "dispersion" */
605 strncpy(req.data, getvar, MAX_CM_SIZE-1); 654 strncpy(req.data, getvar, MAX_CM_SIZE - 1);
606 req.count = htons(strlen(getvar)); 655 req.count = htons(strlen(getvar));
607 DBG(printf("sending READVAR request...\n")); 656 DBG(printf("sending READVAR request...\n"));
608 write(conn, &req, SIZEOF_NTPCM(req)); 657 write(conn, &req, SIZEOF_NTPCM(req));
@@ -613,8 +662,11 @@ double jitter_request(int *status){
613 read(conn, &req, SIZEOF_NTPCM(req)); 662 read(conn, &req, SIZEOF_NTPCM(req));
614 DBG(print_ntp_control_message(&req)); 663 DBG(print_ntp_control_message(&req));
615 664
616 if(req.op&REM_ERROR && strstr(getvar, "jitter")) { 665 if (req.op & REM_ERROR && strstr(getvar, "jitter")) {
617 if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n"); 666 if (verbose) {
667 printf("The 'jitter' command failed (old ntp server?)\nRestarting with "
668 "'dispersion'...\n");
669 }
618 getvar = "dispersion"; 670 getvar = "dispersion";
619 num_selected--; 671 num_selected--;
620 i--; 672 i--;
@@ -622,32 +674,33 @@ double jitter_request(int *status){
622 } 674 }
623 675
624 /* get to the float value */ 676 /* get to the float value */
625 if(verbose) { 677 if (verbose) {
626 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); 678 printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
627 } 679 }
628 if((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)){ 680 if ((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)) {
629 die(STATE_UNKNOWN, 681 die(STATE_UNKNOWN, _("jitter response too large (%lu bytes)\n"),
630 _("jitter response too large (%lu bytes)\n"), 682 (unsigned long)jitter_data_count);
631 (unsigned long)jitter_data_count);
632 } 683 }
633 memcpy(jitter_data, req.data, jitter_data_count); 684 memcpy(jitter_data, req.data, jitter_data_count);
634 jitter_data[jitter_data_count] = '\0'; 685 jitter_data[jitter_data_count] = '\0';
635 startofvalue = strchr(jitter_data, '='); 686 startofvalue = strchr(jitter_data, '=');
636 if(startofvalue != NULL) { 687 if (startofvalue != NULL) {
637 startofvalue++; 688 startofvalue++;
638 jitter = strtod(startofvalue, &nptr); 689 jitter = strtod(startofvalue, &nptr);
639 } 690 }
640 if(startofvalue == NULL || startofvalue==nptr){ 691 if (startofvalue == NULL || startofvalue == nptr) {
641 printf("warning: unable to read server jitter response.\n"); 692 printf("warning: unable to read server jitter response.\n");
642 *status = STATE_UNKNOWN; 693 *status = STATE_UNKNOWN;
643 } else { 694 } else {
644 if(verbose) printf("%g\n", jitter); 695 if (verbose) {
696 printf("%g\n", jitter);
697 }
645 num_valid++; 698 num_valid++;
646 rval += jitter; 699 rval += jitter;
647 } 700 }
648 } 701 }
649 } 702 }
650 if(verbose){ 703 if (verbose) {
651 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected); 704 printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
652 } 705 }
653 } 706 }
@@ -655,37 +708,33 @@ double jitter_request(int *status){
655 rval = num_valid ? rval / num_valid : -1.0; 708 rval = num_valid ? rval / num_valid : -1.0;
656 709
657 close(conn); 710 close(conn);
658 if(peers!=NULL) free(peers); 711 if (peers != NULL) {
712 free(peers);
713 }
659 /* If we return -1.0, it means no synchronization source was found */ 714 /* If we return -1.0, it means no synchronization source was found */
660 return rval; 715 return rval;
661} 716}
662 717
663int process_arguments(int argc, char **argv){ 718int process_arguments(int argc, char **argv) {
664 int c; 719 int c;
665 int option=0; 720 int option = 0;
666 static struct option longopts[] = { 721 static struct option longopts[] = {
667 {"version", no_argument, 0, 'V'}, 722 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
668 {"help", no_argument, 0, 'h'}, 723 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
669 {"verbose", no_argument, 0, 'v'}, 724 {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'},
670 {"use-ipv4", no_argument, 0, '4'}, 725 {"critical", required_argument, 0, 'c'}, {"jwarn", required_argument, 0, 'j'},
671 {"use-ipv6", no_argument, 0, '6'}, 726 {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'},
672 {"warning", required_argument, 0, 'w'}, 727 {"hostname", required_argument, 0, 'H'}, {0, 0, 0, 0}};
673 {"critical", required_argument, 0, 'c'}, 728
674 {"jwarn", required_argument, 0, 'j'}, 729 if (argc < 2) {
675 {"jcrit", required_argument, 0, 'k'}, 730 usage("\n");
676 {"timeout", required_argument, 0, 't'}, 731 }
677 {"hostname", required_argument, 0, 'H'},
678 {0, 0, 0, 0}
679 };
680
681
682 if (argc < 2)
683 usage ("\n");
684 732
685 while (1) { 733 while (1) {
686 c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); 734 c = getopt_long(argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option);
687 if (c == -1 || c == EOF || c == 1) 735 if (c == -1 || c == EOF || c == 1) {
688 break; 736 break;
737 }
689 738
690 switch (c) { 739 switch (c) {
691 case 'h': 740 case 'h':
@@ -716,12 +765,13 @@ int process_arguments(int argc, char **argv){
716 jcrit = optarg; 765 jcrit = optarg;
717 break; 766 break;
718 case 'H': 767 case 'H':
719 if(!is_host(optarg)) 768 if (!is_host(optarg)) {
720 usage2(_("Invalid hostname/address"), optarg); 769 usage2(_("Invalid hostname/address"), optarg);
770 }
721 server_address = strdup(optarg); 771 server_address = strdup(optarg);
722 break; 772 break;
723 case 't': 773 case 't':
724 socket_timeout=atoi(optarg); 774 socket_timeout = atoi(optarg);
725 break; 775 break;
726 case '4': 776 case '4':
727 address_family = AF_INET; 777 address_family = AF_INET;
@@ -730,64 +780,59 @@ int process_arguments(int argc, char **argv){
730#ifdef USE_IPV6 780#ifdef USE_IPV6
731 address_family = AF_INET6; 781 address_family = AF_INET6;
732#else 782#else
733 usage4 (_("IPv6 support not available")); 783 usage4(_("IPv6 support not available"));
734#endif 784#endif
735 break; 785 break;
736 case '?': 786 case '?':
737 /* print short usage statement if args not parsable */ 787 /* print short usage statement if args not parsable */
738 usage5 (); 788 usage5();
739 break; 789 break;
740 } 790 }
741 } 791 }
742 792
743 if(server_address == NULL){ 793 if (server_address == NULL) {
744 usage4(_("Hostname was not supplied")); 794 usage4(_("Hostname was not supplied"));
745 } 795 }
746 796
747 return 0; 797 return 0;
748} 798}
749 799
750char *perfd_offset (double offset) 800char *perfd_offset(double offset) {
751{ 801 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
752 return fperfdata ("offset", offset, "s", 802 offset_thresholds->critical->end, false, 0, false, 0);
753 true, offset_thresholds->warning->end,
754 true, offset_thresholds->critical->end,
755 false, 0, false, 0);
756} 803}
757 804
758char *perfd_jitter (double jitter) 805char *perfd_jitter(double jitter) {
759{ 806 return fperfdata("jitter", jitter, "s", do_jitter, jitter_thresholds->warning->end, do_jitter,
760 return fperfdata ("jitter", jitter, "s", 807 jitter_thresholds->critical->end, true, 0, false, 0);
761 do_jitter, jitter_thresholds->warning->end,
762 do_jitter, jitter_thresholds->critical->end,
763 true, 0, false, 0);
764} 808}
765 809
766int main(int argc, char *argv[]){ 810int main(int argc, char *argv[]) {
767 int result, offset_result, jitter_result; 811 int result, offset_result, jitter_result;
768 double offset=0, jitter=0; 812 double offset = 0, jitter = 0;
769 char *result_line, *perfdata_line; 813 char *result_line, *perfdata_line;
770 814
771 setlocale (LC_ALL, ""); 815 setlocale(LC_ALL, "");
772 bindtextdomain (PACKAGE, LOCALEDIR); 816 bindtextdomain(PACKAGE, LOCALEDIR);
773 textdomain (PACKAGE); 817 textdomain(PACKAGE);
774 818
775 result = offset_result = jitter_result = STATE_OK; 819 result = offset_result = jitter_result = STATE_OK;
776 820
777 /* Parse extra opts if any */ 821 /* Parse extra opts if any */
778 argv=np_extra_opts (&argc, argv, progname); 822 argv = np_extra_opts(&argc, argv, progname);
779 823
780 if (process_arguments (argc, argv) == ERROR) 824 if (process_arguments(argc, argv) == ERROR) {
781 usage4 (_("Could not parse arguments")); 825 usage4(_("Could not parse arguments"));
826 }
782 827
783 set_thresholds(&offset_thresholds, owarn, ocrit); 828 set_thresholds(&offset_thresholds, owarn, ocrit);
784 set_thresholds(&jitter_thresholds, jwarn, jcrit); 829 set_thresholds(&jitter_thresholds, jwarn, jcrit);
785 830
786 /* initialize alarm signal handling */ 831 /* initialize alarm signal handling */
787 signal (SIGALRM, socket_timeout_alarm_handler); 832 signal(SIGALRM, socket_timeout_alarm_handler);
788 833
789 /* set socket timeout */ 834 /* set socket timeout */
790 alarm (socket_timeout); 835 alarm(socket_timeout);
791 836
792 offset = offset_request(server_address, &offset_result); 837 offset = offset_request(server_address, &offset_result);
793 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. 838 /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN.
@@ -803,31 +848,32 @@ int main(int argc, char *argv[]){
803 * servers recognize. Trying to check the jitter on OpenNTPD 848 * servers recognize. Trying to check the jitter on OpenNTPD
804 * (for example) will result in an error 849 * (for example) will result in an error
805 */ 850 */
806 if(do_jitter){ 851 if (do_jitter) {
807 jitter=jitter_request(&jitter_result); 852 jitter = jitter_request(&jitter_result);
808 result = max_state_alt(result, get_status(jitter, jitter_thresholds)); 853 result = max_state_alt(result, get_status(jitter, jitter_thresholds));
809 /* -1 indicates that we couldn't calculate the jitter 854 /* -1 indicates that we couldn't calculate the jitter
810 * Only overrides STATE_OK from the offset */ 855 * Only overrides STATE_OK from the offset */
811 if(jitter == -1.0 && result == STATE_OK) 856 if (jitter == -1.0 && result == STATE_OK) {
812 result = STATE_UNKNOWN; 857 result = STATE_UNKNOWN;
858 }
813 } 859 }
814 result = max_state_alt(result, jitter_result); 860 result = max_state_alt(result, jitter_result);
815 861
816 switch (result) { 862 switch (result) {
817 case STATE_CRITICAL : 863 case STATE_CRITICAL:
818 xasprintf(&result_line, _("NTP CRITICAL:")); 864 xasprintf(&result_line, _("NTP CRITICAL:"));
819 break; 865 break;
820 case STATE_WARNING : 866 case STATE_WARNING:
821 xasprintf(&result_line, _("NTP WARNING:")); 867 xasprintf(&result_line, _("NTP WARNING:"));
822 break; 868 break;
823 case STATE_OK : 869 case STATE_OK:
824 xasprintf(&result_line, _("NTP OK:")); 870 xasprintf(&result_line, _("NTP OK:"));
825 break; 871 break;
826 default : 872 default:
827 xasprintf(&result_line, _("NTP UNKNOWN:")); 873 xasprintf(&result_line, _("NTP UNKNOWN:"));
828 break; 874 break;
829 } 875 }
830 if(offset_result == STATE_UNKNOWN){ 876 if (offset_result == STATE_UNKNOWN) {
831 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 877 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
832 xasprintf(&perfdata_line, ""); 878 xasprintf(&perfdata_line, "");
833 } else { 879 } else {
@@ -836,41 +882,41 @@ int main(int argc, char *argv[]){
836 } 882 }
837 if (do_jitter) { 883 if (do_jitter) {
838 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); 884 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter);
839 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); 885 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter));
840 } 886 }
841 printf("%s|%s\n", result_line, perfdata_line); 887 printf("%s|%s\n", result_line, perfdata_line);
842 888
843 if(server_address!=NULL) free(server_address); 889 if (server_address != NULL) {
890 free(server_address);
891 }
844 return result; 892 return result;
845} 893}
846 894
847 895void print_help(void) {
848
849void print_help(void){
850 print_revision(progname, NP_VERSION); 896 print_revision(progname, NP_VERSION);
851 897
852 printf ("Copyright (c) 2006 Sean Finney\n"); 898 printf("Copyright (c) 2006 Sean Finney\n");
853 printf (COPYRIGHT, copyright, email); 899 printf(COPYRIGHT, copyright, email);
854 900
855 printf ("%s\n", _("This plugin checks the selected ntp server")); 901 printf("%s\n", _("This plugin checks the selected ntp server"));
856 902
857 printf ("\n\n"); 903 printf("\n\n");
858 904
859 print_usage(); 905 print_usage();
860 printf (UT_HELP_VRSN); 906 printf(UT_HELP_VRSN);
861 printf (UT_EXTRA_OPTS); 907 printf(UT_EXTRA_OPTS);
862 printf (UT_HOST_PORT, 'p', "123"); 908 printf(UT_HOST_PORT, 'p', "123");
863 printf (UT_IPv46); 909 printf(UT_IPv46);
864 printf (" %s\n", "-w, --warning=THRESHOLD"); 910 printf(" %s\n", "-w, --warning=THRESHOLD");
865 printf (" %s\n", _("Offset to result in warning status (seconds)")); 911 printf(" %s\n", _("Offset to result in warning status (seconds)"));
866 printf (" %s\n", "-c, --critical=THRESHOLD"); 912 printf(" %s\n", "-c, --critical=THRESHOLD");
867 printf (" %s\n", _("Offset to result in critical status (seconds)")); 913 printf(" %s\n", _("Offset to result in critical status (seconds)"));
868 printf (" %s\n", "-j, --jwarn=THRESHOLD"); 914 printf(" %s\n", "-j, --jwarn=THRESHOLD");
869 printf (" %s\n", _("Warning threshold for jitter")); 915 printf(" %s\n", _("Warning threshold for jitter"));
870 printf (" %s\n", "-k, --jcrit=THRESHOLD"); 916 printf(" %s\n", "-k, --jcrit=THRESHOLD");
871 printf (" %s\n", _("Critical threshold for jitter")); 917 printf(" %s\n", _("Critical threshold for jitter"));
872 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 918 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
873 printf (UT_VERBOSE); 919 printf(UT_VERBOSE);
874 920
875 printf("\n"); 921 printf("\n");
876 printf("%s\n", _("Notes:")); 922 printf("%s\n", _("Notes:"));
@@ -881,21 +927,21 @@ void print_help(void){
881 printf(" %s\n", _("Normal offset check:")); 927 printf(" %s\n", _("Normal offset check:"));
882 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); 928 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
883 printf("\n"); 929 printf("\n");
884 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 930 printf(" %s\n",
931 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
885 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 932 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
886 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); 933 printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
887 934
888 printf (UT_SUPPORT); 935 printf(UT_SUPPORT);
889 936
890 printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); 937 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
891 printf ("%s\n\n", _("check_ntp_time instead.")); 938 printf("%s\n\n", _("check_ntp_time instead."));
892} 939}
893 940
894void 941void print_usage(void) {
895print_usage(void) 942 printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
896{ 943 printf("%s\n\n", _("check_ntp_time instead."));
897 printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); 944 printf("%s\n", _("Usage:"));
898 printf ("%s\n\n", _("check_ntp_time instead.")); 945 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n",
899 printf ("%s\n", _("Usage:")); 946 progname);
900 printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-4|-6] [-v verbose]\n", progname);
901} 947}
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index f99e5032..24d1c9b5 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -35,6 +35,7 @@
35 * 35 *
36 *****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
38const char *progname = "check_ntp_peer"; 39const char *progname = "check_ntp_peer";
39const char *copyright = "2006-2024"; 40const char *copyright = "2006-2024";
40const char *email = "devel@monitoring-plugins.org"; 41const char *email = "devel@monitoring-plugins.org";
@@ -42,30 +43,18 @@ const char *email = "devel@monitoring-plugins.org";
42#include "common.h" 43#include "common.h"
43#include "netutils.h" 44#include "netutils.h"
44#include "utils.h" 45#include "utils.h"
46#include "../lib/states.h"
47#include "check_ntp_peer.d/config.h"
45 48
46static char *server_address = NULL;
47static int port = 123;
48static int verbose = 0; 49static int verbose = 0;
49static bool quiet = false;
50static char *owarn = "60";
51static char *ocrit = "120";
52static bool do_stratum = false;
53static char *swarn = "-1:16";
54static char *scrit = "-1:16";
55static bool do_jitter = false;
56static char *jwarn = "-1:5000";
57static char *jcrit = "-1:10000";
58static bool do_truechimers = false;
59static char *twarn = "0:";
60static char *tcrit = "0:";
61static bool syncsource_found = false; 50static bool syncsource_found = false;
62static bool li_alarm = false; 51static bool li_alarm = false;
63 52
64static int process_arguments(int /*argc*/, char ** /*argv*/); 53typedef struct {
65static thresholds *offset_thresholds = NULL; 54 int errorcode;
66static thresholds *jitter_thresholds = NULL; 55 check_ntp_peer_config config;
67static thresholds *stratum_thresholds = NULL; 56} check_ntp_peer_config_wrapper;
68static thresholds *truechimer_thresholds = NULL; 57static check_ntp_peer_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
69static void print_help(void); 58static void print_help(void);
70void print_usage(void); 59void print_usage(void);
71 60
@@ -94,9 +83,9 @@ typedef struct {
94/* bits 1,2 are the leap indicator */ 83/* bits 1,2 are the leap indicator */
95#define LI_MASK 0xc0 84#define LI_MASK 0xc0
96#define LI(x) ((x & LI_MASK) >> 6) 85#define LI(x) ((x & LI_MASK) >> 6)
97#define LI_SET(x, y) \ 86#define LI_SET(x, y) \
98 do { \ 87 do { \
99 x |= ((y << 6) & LI_MASK); \ 88 x |= ((y << 6) & LI_MASK); \
100 } while (0) 89 } while (0)
101/* and these are the values of the leap indicator */ 90/* and these are the values of the leap indicator */
102#define LI_NOWARNING 0x00 91#define LI_NOWARNING 0x00
@@ -106,17 +95,17 @@ typedef struct {
106/* bits 3,4,5 are the ntp version */ 95/* bits 3,4,5 are the ntp version */
107#define VN_MASK 0x38 96#define VN_MASK 0x38
108#define VN(x) ((x & VN_MASK) >> 3) 97#define VN(x) ((x & VN_MASK) >> 3)
109#define VN_SET(x, y) \ 98#define VN_SET(x, y) \
110 do { \ 99 do { \
111 x |= ((y << 3) & VN_MASK); \ 100 x |= ((y << 3) & VN_MASK); \
112 } while (0) 101 } while (0)
113#define VN_RESERVED 0x02 102#define VN_RESERVED 0x02
114/* bits 6,7,8 are the ntp mode */ 103/* bits 6,7,8 are the ntp mode */
115#define MODE_MASK 0x07 104#define MODE_MASK 0x07
116#define MODE(x) (x & MODE_MASK) 105#define MODE(x) (x & MODE_MASK)
117#define MODE_SET(x, y) \ 106#define MODE_SET(x, y) \
118 do { \ 107 do { \
119 x |= (y & MODE_MASK); \ 108 x |= (y & MODE_MASK); \
120 } while (0) 109 } while (0)
121/* here are some values */ 110/* here are some values */
122#define MODE_CLIENT 0x03 111#define MODE_CLIENT 0x03
@@ -128,9 +117,9 @@ typedef struct {
128#define REM_MORE 0x20 117#define REM_MORE 0x20
129/* In control message, bits 11 - 15 are opcode */ 118/* In control message, bits 11 - 15 are opcode */
130#define OP_MASK 0x1f 119#define OP_MASK 0x1f
131#define OP_SET(x, y) \ 120#define OP_SET(x, y) \
132 do { \ 121 do { \
133 x |= (y & OP_MASK); \ 122 x |= (y & OP_MASK); \
134 } while (0) 123 } while (0)
135#define OP_READSTAT 0x01 124#define OP_READSTAT 0x01
136#define OP_READVAR 0x02 125#define OP_READVAR 0x02
@@ -143,39 +132,40 @@ typedef struct {
143/* NTP control message header is 12 bytes, plus any data in the data 132/* NTP control message header is 12 bytes, plus any data in the data
144 * field, plus null padding to the nearest 32-bit boundary per rfc. 133 * field, plus null padding to the nearest 32-bit boundary per rfc.
145 */ 134 */
146#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) 135#define SIZEOF_NTPCM(m) \
136 (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0))
147 137
148/* finally, a little helper or two for debugging: */ 138/* finally, a little helper or two for debugging: */
149#define DBG(x) \ 139#define DBG(x) \
150 do { \ 140 do { \
151 if (verbose > 1) { \ 141 if (verbose > 1) { \
152 x; \ 142 x; \
153 } \ 143 } \
154 } while (0); 144 } while (0);
155#define PRINTSOCKADDR(x) \ 145#define PRINTSOCKADDR(x) \
156 do { \ 146 do { \
157 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ 147 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
158 } while (0); 148 } while (0);
159 149
160void print_ntp_control_message(const ntp_control_message *p) { 150void print_ntp_control_message(const ntp_control_message *message) {
161 printf("control packet contents:\n"); 151 printf("control packet contents:\n");
162 printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); 152 printf("\tflags: 0x%.2x , 0x%.2x\n", message->flags, message->op);
163 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); 153 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
164 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); 154 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
165 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); 155 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
166 printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP); 156 printf("\t response=%d (0x%.2x)\n", (message->op & REM_RESP) > 0, message->op & REM_RESP);
167 printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE); 157 printf("\t more=%d (0x%.2x)\n", (message->op & REM_MORE) > 0, message->op & REM_MORE);
168 printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR); 158 printf("\t error=%d (0x%.2x)\n", (message->op & REM_ERROR) > 0, message->op & REM_ERROR);
169 printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK); 159 printf("\t op=%d (0x%.2x)\n", message->op & OP_MASK, message->op & OP_MASK);
170 printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); 160 printf("\tsequence: %d (0x%.2x)\n", ntohs(message->seq), ntohs(message->seq));
171 printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); 161 printf("\tstatus: %d (0x%.2x)\n", ntohs(message->status), ntohs(message->status));
172 printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); 162 printf("\tassoc: %d (0x%.2x)\n", ntohs(message->assoc), ntohs(message->assoc));
173 printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); 163 printf("\toffset: %d (0x%.2x)\n", ntohs(message->offset), ntohs(message->offset));
174 printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); 164 printf("\tcount: %d (0x%.2x)\n", ntohs(message->count), ntohs(message->count));
175 165
176 int numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair)); 166 int numpeers = ntohs(message->count) / (sizeof(ntp_assoc_status_pair));
177 if (p->op & REM_RESP && p->op & OP_READSTAT) { 167 if (message->op & REM_RESP && message->op & OP_READSTAT) {
178 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)p->data; 168 const ntp_assoc_status_pair *peer = (ntp_assoc_status_pair *)message->data;
179 for (int i = 0; i < numpeers; i++) { 169 for (int i = 0; i < numpeers; i++) {
180 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); 170 printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status));
181 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { 171 if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) {
@@ -190,13 +180,13 @@ void print_ntp_control_message(const ntp_control_message *p) {
190 } 180 }
191} 181}
192 182
193void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq) { 183void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_t seq) {
194 memset(p, 0, sizeof(ntp_control_message)); 184 memset(message, 0, sizeof(ntp_control_message));
195 LI_SET(p->flags, LI_NOWARNING); 185 LI_SET(message->flags, LI_NOWARNING);
196 VN_SET(p->flags, VN_RESERVED); 186 VN_SET(message->flags, VN_RESERVED);
197 MODE_SET(p->flags, MODE_CONTROLMSG); 187 MODE_SET(message->flags, MODE_CONTROLMSG);
198 OP_SET(p->op, opcode); 188 OP_SET(message->op, opcode);
199 p->seq = htons(seq); 189 message->seq = htons(seq);
200 /* Remaining fields are zero for requests */ 190 /* Remaining fields are zero for requests */
201} 191}
202 192
@@ -211,10 +201,23 @@ void setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq)
211 * status is pretty much useless as syncsource_found is a global variable 201 * status is pretty much useless as syncsource_found is a global variable
212 * used later in main to check is the server was synchronized. It works 202 * used later in main to check is the server was synchronized. It works
213 * so I left it alone */ 203 * so I left it alone */
214int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum, int *num_truechimers) { 204typedef struct {
215 *offset_result = STATE_UNKNOWN; 205 mp_state_enum state;
216 *jitter = *stratum = -1; 206 mp_state_enum offset_result;
217 *num_truechimers = 0; 207 double offset;
208 double jitter;
209 long stratum;
210 int num_truechimers;
211} ntp_request_result;
212ntp_request_result ntp_request(const check_ntp_peer_config config) {
213
214 ntp_request_result result = {
215 .state = STATE_OK,
216 .offset_result = STATE_UNKNOWN,
217 .jitter = -1,
218 .stratum = -1,
219 .num_truechimers = 0,
220 };
218 221
219 /* Long-winded explanation: 222 /* Long-winded explanation:
220 * Getting the sync peer offset, jitter and stratum requires a number of 223 * Getting the sync peer offset, jitter and stratum requires a number of
@@ -237,10 +240,10 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
237 void *tmp; 240 void *tmp;
238 ntp_assoc_status_pair *peers = NULL; 241 ntp_assoc_status_pair *peers = NULL;
239 int peer_offset = 0; 242 int peer_offset = 0;
240 int peers_size = 0; 243 size_t peers_size = 0;
241 int npeers = 0; 244 size_t npeers = 0;
242 int conn = -1; 245 int conn = -1;
243 my_udp_connect(server_address, port, &conn); 246 my_udp_connect(config.server_address, config.port, &conn);
244 247
245 /* keep sending requests until the server stops setting the 248 /* keep sending requests until the server stops setting the
246 * REM_MORE bit, though usually this is only 1 packet. */ 249 * REM_MORE bit, though usually this is only 1 packet. */
@@ -255,24 +258,28 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
255 /* Attempt to read the largest size packet possible */ 258 /* Attempt to read the largest size packet possible */
256 req.count = htons(MAX_CM_SIZE); 259 req.count = htons(MAX_CM_SIZE);
257 DBG(printf("receiving READSTAT response")) 260 DBG(printf("receiving READSTAT response"))
258 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) 261 if (read(conn, &req, SIZEOF_NTPCM(req)) == -1) {
259 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 262 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
263 }
260 DBG(print_ntp_control_message(&req)); 264 DBG(print_ntp_control_message(&req));
261 /* discard obviously invalid packets */ 265 /* discard obviously invalid packets */
262 if (ntohs(req.count) > MAX_CM_SIZE) 266 if (ntohs(req.count) > MAX_CM_SIZE) {
263 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n"); 267 die(STATE_CRITICAL, "NTP CRITICAL: Invalid packet received from NTP server\n");
268 }
264 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); 269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
265 270
266 if (LI(req.flags) == LI_ALARM) 271 if (LI(req.flags) == LI_ALARM) {
267 li_alarm = true; 272 li_alarm = true;
273 }
268 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
269 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
270 */ 276 */
271 peers_size += ntohs(req.count); 277 peers_size += ntohs(req.count);
272 if ((tmp = realloc(peers, peers_size)) == NULL) 278 if ((tmp = realloc(peers, peers_size)) == NULL) {
273 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 279 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
280 }
274 peers = tmp; 281 peers = tmp;
275 memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count)); 282 memcpy((peers + peer_offset), (void *)req.data, ntohs(req.count));
276 npeers = peers_size / sizeof(ntp_assoc_status_pair); 283 npeers = peers_size / sizeof(ntp_assoc_status_pair);
277 peer_offset += ntohs(req.count); 284 peer_offset += ntohs(req.count);
278 } while (req.op & REM_MORE); 285 } while (req.op & REM_MORE);
@@ -280,9 +287,9 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
280 /* first, let's find out if we have a sync source, or if there are 287 /* first, let's find out if we have a sync source, or if there are
281 * at least some candidates. In the latter case we'll issue 288 * at least some candidates. In the latter case we'll issue
282 * a warning but go ahead with the check on them. */ 289 * a warning but go ahead with the check on them. */
283 for (int i = 0; i < npeers; i++) { 290 for (size_t i = 0; i < npeers; i++) {
284 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { 291 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
285 (*num_truechimers)++; 292 result.num_truechimers++;
286 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { 293 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
287 num_candidates++; 294 num_candidates++;
288 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { 295 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
@@ -293,31 +300,35 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
293 } 300 }
294 } 301 }
295 302
296 if (verbose) 303 if (verbose) {
297 printf("%d candidate peers available\n", num_candidates); 304 printf("%d candidate peers available\n", num_candidates);
298 if (verbose && syncsource_found) 305 }
306 if (verbose && syncsource_found) {
299 printf("synchronization source found\n"); 307 printf("synchronization source found\n");
308 }
300 309
301 int status = STATE_OK;
302 if (!syncsource_found) { 310 if (!syncsource_found) {
303 status = STATE_WARNING; 311 result.state = STATE_WARNING;
304 if (verbose) 312 if (verbose) {
305 printf("warning: no synchronization source found\n"); 313 printf("warning: no synchronization source found\n");
314 }
306 } 315 }
307 if (li_alarm) { 316 if (li_alarm) {
308 status = STATE_WARNING; 317 result.state = STATE_WARNING;
309 if (verbose) 318 if (verbose) {
310 printf("warning: LI_ALARM bit is set\n"); 319 printf("warning: LI_ALARM bit is set\n");
320 }
311 } 321 }
312 322
313 const char *getvar = "stratum,offset,jitter"; 323 const char *getvar = "stratum,offset,jitter";
314 char *data; 324 char *data;
315 for (int i = 0; i < npeers; i++) { 325 for (size_t i = 0; i < npeers; i++) {
316 /* Only query this server if it is the current sync source */ 326 /* Only query this server if it is the current sync source */
317 /* If there's no sync.peer, query all candidates and use the best one */ 327 /* If there's no sync.peer, query all candidates and use the best one */
318 if (PEER_SEL(peers[i].status) >= min_peer_sel) { 328 if (PEER_SEL(peers[i].status) >= min_peer_sel) {
319 if (verbose) 329 if (verbose) {
320 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 330 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
331 }
321 xasprintf(&data, ""); 332 xasprintf(&data, "");
322 do { 333 do {
323 setup_control_request(&req, OP_READVAR, 2); 334 setup_control_request(&req, OP_READVAR, 2);
@@ -342,81 +353,95 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
342 DBG(print_ntp_control_message(&req)); 353 DBG(print_ntp_control_message(&req));
343 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2)); 354 } while (!(req.op & OP_READVAR && ntohs(req.seq) == 2));
344 355
345 if (!(req.op & REM_ERROR)) 356 if (!(req.op & REM_ERROR)) {
346 xasprintf(&data, "%s%s", data, req.data); 357 xasprintf(&data, "%s%s", data, req.data);
358 }
347 } while (req.op & REM_MORE); 359 } while (req.op & REM_MORE);
348 360
349 if (req.op & REM_ERROR) { 361 if (req.op & REM_ERROR) {
350 if (strstr(getvar, "jitter")) { 362 if (strstr(getvar, "jitter")) {
351 if (verbose) 363 if (verbose) {
352 printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with " 364 printf("The command failed. This is usually caused by servers refusing the "
365 "'jitter'\nvariable. Restarting with "
353 "'dispersion'...\n"); 366 "'dispersion'...\n");
367 }
354 getvar = "stratum,offset,dispersion"; 368 getvar = "stratum,offset,dispersion";
355 i--; 369 i--;
356 continue; 370 continue;
357 } 371 }
358 if (strlen(getvar)) { 372 if (strlen(getvar)) {
359 if (verbose) 373 if (verbose) {
360 printf("Server didn't like dispersion either; will retrieve everything\n"); 374 printf("Server didn't like dispersion either; will retrieve everything\n");
375 }
361 getvar = ""; 376 getvar = "";
362 i--; 377 i--;
363 continue; 378 continue;
364 } 379 }
365 } 380 }
366 381
367 if (verbose > 1) 382 if (verbose > 1) {
368 printf("Server responded: >>>%s<<<\n", data); 383 printf("Server responded: >>>%s<<<\n", data);
384 }
369 385
370 double tmp_offset = 0; 386 double tmp_offset = 0;
371 char *value; 387 char *value;
372 char *nptr; 388 char *nptr;
373 /* get the offset */ 389 /* get the offset */
374 if (verbose) 390 if (verbose) {
375 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc)); 391 printf("parsing offset from peer %.2x: ", ntohs(peers[i].assoc));
392 }
376 393
377 value = np_extract_ntpvar(data, "offset"); 394 value = np_extract_ntpvar(data, "offset");
378 nptr = NULL; 395 nptr = NULL;
379 /* Convert the value if we have one */ 396 /* Convert the value if we have one */
380 if (value != NULL) 397 if (value != NULL) {
381 tmp_offset = strtod(value, &nptr) / 1000; 398 tmp_offset = strtod(value, &nptr) / 1000;
399 }
382 /* If value is null or no conversion was performed */ 400 /* If value is null or no conversion was performed */
383 if (value == NULL || value == nptr) { 401 if (value == NULL || value == nptr) {
384 if (verbose) 402 if (verbose) {
385 printf("error: unable to read server offset response.\n"); 403 printf("error: unable to read server offset response.\n");
404 }
386 } else { 405 } else {
387 if (verbose) 406 if (verbose) {
388 printf("%.10g\n", tmp_offset); 407 printf("%.10g\n", tmp_offset);
389 if (*offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(*offset)) { 408 }
390 *offset = tmp_offset; 409 if (result.offset_result == STATE_UNKNOWN ||
391 *offset_result = STATE_OK; 410 fabs(tmp_offset) < fabs(result.offset)) {
411 result.offset = tmp_offset;
412 result.offset_result = STATE_OK;
392 } else { 413 } else {
393 /* Skip this one; move to the next */ 414 /* Skip this one; move to the next */
394 continue; 415 continue;
395 } 416 }
396 } 417 }
397 418
398 if (do_jitter) { 419 if (config.do_jitter) {
399 /* get the jitter */ 420 /* get the jitter */
400 if (verbose) { 421 if (verbose) {
401 printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", 422 printf("parsing %s from peer %.2x: ",
423 strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter",
402 ntohs(peers[i].assoc)); 424 ntohs(peers[i].assoc));
403 } 425 }
404 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); 426 value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion"
427 : "jitter");
405 nptr = NULL; 428 nptr = NULL;
406 /* Convert the value if we have one */ 429 /* Convert the value if we have one */
407 if (value != NULL) 430 if (value != NULL) {
408 *jitter = strtod(value, &nptr); 431 result.jitter = strtod(value, &nptr);
432 }
409 /* If value is null or no conversion was performed */ 433 /* If value is null or no conversion was performed */
410 if (value == NULL || value == nptr) { 434 if (value == NULL || value == nptr) {
411 if (verbose) 435 if (verbose) {
412 printf("error: unable to read server jitter/dispersion response.\n"); 436 printf("error: unable to read server jitter/dispersion response.\n");
413 *jitter = -1; 437 }
438 result.jitter = -1;
414 } else if (verbose) { 439 } else if (verbose) {
415 printf("%.10g\n", *jitter); 440 printf("%.10g\n", result.jitter);
416 } 441 }
417 } 442 }
418 443
419 if (do_stratum) { 444 if (config.do_stratum) {
420 /* get the stratum */ 445 /* get the stratum */
421 if (verbose) { 446 if (verbose) {
422 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc)); 447 printf("parsing stratum from peer %.2x: ", ntohs(peers[i].assoc));
@@ -424,44 +449,59 @@ int ntp_request(double *offset, int *offset_result, double *jitter, int *stratum
424 value = np_extract_ntpvar(data, "stratum"); 449 value = np_extract_ntpvar(data, "stratum");
425 nptr = NULL; 450 nptr = NULL;
426 /* Convert the value if we have one */ 451 /* Convert the value if we have one */
427 if (value != NULL) 452 if (value != NULL) {
428 *stratum = strtol(value, &nptr, 10); 453 result.stratum = strtol(value, &nptr, 10);
454 }
429 if (value == NULL || value == nptr) { 455 if (value == NULL || value == nptr) {
430 if (verbose) 456 if (verbose) {
431 printf("error: unable to read server stratum response.\n"); 457 printf("error: unable to read server stratum response.\n");
432 *stratum = -1; 458 }
459 result.stratum = -1;
433 } else { 460 } else {
434 if (verbose) 461 if (verbose) {
435 printf("%i\n", *stratum); 462 printf("%li\n", result.stratum);
463 }
436 } 464 }
437 } 465 }
438 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */ 466 } /* if (PEER_SEL(peers[i].status) >= min_peer_sel) */
439 } /* for (i = 0; i < npeers; i++) */ 467 } /* for (i = 0; i < npeers; i++) */
440 468
441 close(conn); 469 close(conn);
442 if (peers != NULL) 470 if (peers != NULL) {
443 free(peers); 471 free(peers);
472 }
444 473
445 return status; 474 return result;
446} 475}
447 476
448int process_arguments(int argc, char **argv) { 477check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
449 static struct option longopts[] = { 478 static struct option longopts[] = {
450 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, 479 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'},
451 {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, 480 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'},
452 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"swarn", required_argument, 0, 'W'}, 481 {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'},
453 {"scrit", required_argument, 0, 'C'}, {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, 482 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'},
454 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'}, 483 {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'},
455 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; 484 {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'},
456 485 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'},
457 if (argc < 2) 486 {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'},
487 {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}};
488
489 if (argc < 2) {
458 usage("\n"); 490 usage("\n");
491 }
492
493 check_ntp_peer_config_wrapper result = {
494 .errorcode = OK,
495 .config = check_ntp_peer_config_init(),
496 };
459 497
460 while (true) { 498 while (true) {
461 int option = 0; 499 int option = 0;
462 int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); 500 int option_char =
463 if (option_char == -1 || option_char == EOF || option_char == 1) 501 getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option);
502 if (option_char == -1 || option_char == EOF || option_char == 1) {
464 break; 503 break;
504 }
465 505
466 switch (option_char) { 506 switch (option_char) {
467 case 'h': 507 case 'h':
@@ -476,45 +516,46 @@ int process_arguments(int argc, char **argv) {
476 verbose++; 516 verbose++;
477 break; 517 break;
478 case 'q': 518 case 'q':
479 quiet = true; 519 result.config.quiet = true;
480 break; 520 break;
481 case 'w': 521 case 'w':
482 owarn = optarg; 522 result.config.owarn = optarg;
483 break; 523 break;
484 case 'c': 524 case 'c':
485 ocrit = optarg; 525 result.config.ocrit = optarg;
486 break; 526 break;
487 case 'W': 527 case 'W':
488 do_stratum = true; 528 result.config.do_stratum = true;
489 swarn = optarg; 529 result.config.swarn = optarg;
490 break; 530 break;
491 case 'C': 531 case 'C':
492 do_stratum = true; 532 result.config.do_stratum = true;
493 scrit = optarg; 533 result.config.scrit = optarg;
494 break; 534 break;
495 case 'j': 535 case 'j':
496 do_jitter = true; 536 result.config.do_jitter = true;
497 jwarn = optarg; 537 result.config.jwarn = optarg;
498 break; 538 break;
499 case 'k': 539 case 'k':
500 do_jitter = true; 540 result.config.do_jitter = true;
501 jcrit = optarg; 541 result.config.jcrit = optarg;
502 break; 542 break;
503 case 'm': 543 case 'm':
504 do_truechimers = true; 544 result.config.do_truechimers = true;
505 twarn = optarg; 545 result.config.twarn = optarg;
506 break; 546 break;
507 case 'n': 547 case 'n':
508 do_truechimers = true; 548 result.config.do_truechimers = true;
509 tcrit = optarg; 549 result.config.tcrit = optarg;
510 break; 550 break;
511 case 'H': 551 case 'H':
512 if (!is_host(optarg)) 552 if (!is_host(optarg)) {
513 usage2(_("Invalid hostname/address"), optarg); 553 usage2(_("Invalid hostname/address"), optarg);
514 server_address = strdup(optarg); 554 }
555 result.config.server_address = strdup(optarg);
515 break; 556 break;
516 case 'p': 557 case 'p':
517 port = atoi(optarg); 558 result.config.port = atoi(optarg);
518 break; 559 break;
519 case 't': 560 case 't':
520 socket_timeout = atoi(optarg); 561 socket_timeout = atoi(optarg);
@@ -536,30 +577,37 @@ int process_arguments(int argc, char **argv) {
536 } 577 }
537 } 578 }
538 579
539 if (server_address == NULL) { 580 if (result.config.server_address == NULL) {
540 usage4(_("Hostname was not supplied")); 581 usage4(_("Hostname was not supplied"));
541 } 582 }
542 583
543 return 0; 584 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
585 set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit);
586 set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit);
587 set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit);
588
589 return result;
544} 590}
545 591
546char *perfd_offset(double offset) { 592char *perfd_offset(double offset, thresholds *offset_thresholds) {
547 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, 593 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
548 0); 594 offset_thresholds->critical->end, false, 0, false, 0);
549} 595}
550 596
551char *perfd_jitter(double jitter) { 597char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) {
552 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0, 598 return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter,
553 false, 0); 599 jitter_thresholds->critical->end, true, 0, false, 0);
554} 600}
555 601
556char *perfd_stratum(int stratum) { 602char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) {
557 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum, 603 return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end,
558 (int)stratum_thresholds->critical->end, true, 0, true, 16); 604 do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16);
559} 605}
560 606
561char *perfd_truechimers(int num_truechimers) { 607char *perfd_truechimers(int num_truechimers, const bool do_truechimers,
562 return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers, 608 thresholds *truechimer_thresholds) {
609 return perfdata("truechimers", num_truechimers, "", do_truechimers,
610 (int)truechimer_thresholds->warning->end, do_truechimers,
563 (int)truechimer_thresholds->critical->end, true, 0, false, 0); 611 (int)truechimer_thresholds->critical->end, true, 0, false, 0);
564} 612}
565 613
@@ -571,13 +619,13 @@ int main(int argc, char *argv[]) {
571 /* Parse extra opts if any */ 619 /* Parse extra opts if any */
572 argv = np_extra_opts(&argc, argv, progname); 620 argv = np_extra_opts(&argc, argv, progname);
573 621
574 if (process_arguments(argc, argv) == ERROR) 622 check_ntp_peer_config_wrapper tmp_config = process_arguments(argc, argv);
623
624 if (tmp_config.errorcode == ERROR) {
575 usage4(_("Could not parse arguments")); 625 usage4(_("Could not parse arguments"));
626 }
576 627
577 set_thresholds(&offset_thresholds, owarn, ocrit); 628 const check_ntp_peer_config config = tmp_config.config;
578 set_thresholds(&jitter_thresholds, jwarn, jcrit);
579 set_thresholds(&stratum_thresholds, swarn, scrit);
580 set_thresholds(&truechimer_thresholds, twarn, tcrit);
581 629
582 /* initialize alarm signal handling */ 630 /* initialize alarm signal handling */
583 signal(SIGALRM, socket_timeout_alarm_handler); 631 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -585,44 +633,40 @@ int main(int argc, char *argv[]) {
585 /* set socket timeout */ 633 /* set socket timeout */
586 alarm(socket_timeout); 634 alarm(socket_timeout);
587 635
588 int offset_result;
589 int stratum;
590 int num_truechimers;
591 double offset = 0;
592 double jitter = 0;
593 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 636 /* This returns either OK or WARNING (See comment preceding ntp_request) */
594 int result = ntp_request(&offset, &offset_result, &jitter, &stratum, &num_truechimers); 637 ntp_request_result ntp_res = ntp_request(config);
638 mp_state_enum result = STATE_UNKNOWN;
595 639
596 if (offset_result == STATE_UNKNOWN) { 640 if (ntp_res.offset_result == STATE_UNKNOWN) {
597 /* if there's no sync peer (this overrides ntp_request output): */ 641 /* if there's no sync peer (this overrides ntp_request output): */
598 result = (quiet ? STATE_UNKNOWN : STATE_CRITICAL); 642 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL);
599 } else { 643 } else {
600 /* Be quiet if there's no candidates either */ 644 /* Be quiet if there's no candidates either */
601 if (quiet && result == STATE_WARNING) 645 if (config.quiet && result == STATE_WARNING) {
602 result = STATE_UNKNOWN; 646 result = STATE_UNKNOWN;
603 result = max_state_alt(result, get_status(fabs(offset), offset_thresholds)); 647 }
648 result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds));
604 } 649 }
605 650
606 int oresult = result; 651 mp_state_enum oresult = result;
607 652 mp_state_enum tresult = STATE_UNKNOWN;
608 int tresult = STATE_UNKNOWN;
609 653
610 if (do_truechimers) { 654 if (config.do_truechimers) {
611 tresult = get_status(num_truechimers, truechimer_thresholds); 655 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds);
612 result = max_state_alt(result, tresult); 656 result = max_state_alt(result, tresult);
613 } 657 }
614 658
615 int sresult = STATE_UNKNOWN; 659 mp_state_enum sresult = STATE_UNKNOWN;
616 660
617 if (do_stratum) { 661 if (config.do_stratum) {
618 sresult = get_status(stratum, stratum_thresholds); 662 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
619 result = max_state_alt(result, sresult); 663 result = max_state_alt(result, sresult);
620 } 664 }
621 665
622 int jresult = STATE_UNKNOWN; 666 mp_state_enum jresult = STATE_UNKNOWN;
623 667
624 if (do_jitter) { 668 if (config.do_jitter) {
625 jresult = get_status(jitter, jitter_thresholds); 669 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
626 result = max_state_alt(result, jresult); 670 result = max_state_alt(result, jresult);
627 } 671 }
628 672
@@ -641,59 +685,74 @@ int main(int argc, char *argv[]) {
641 xasprintf(&result_line, _("NTP UNKNOWN:")); 685 xasprintf(&result_line, _("NTP UNKNOWN:"));
642 break; 686 break;
643 } 687 }
644 if (!syncsource_found) 688
689 if (!syncsource_found) {
645 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 690 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized"));
646 else if (li_alarm) 691 } else if (li_alarm) {
647 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 692 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set"));
693 }
648 694
649 char *perfdata_line; 695 char *perfdata_line;
650 if (offset_result == STATE_UNKNOWN) { 696 if (ntp_res.offset_result == STATE_UNKNOWN) {
651 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 697 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
652 xasprintf(&perfdata_line, ""); 698 xasprintf(&perfdata_line, "");
653 } else if (oresult == STATE_WARNING) { 699 } else if (oresult == STATE_WARNING) {
654 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), offset); 700 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"),
701 ntp_res.offset);
655 } else if (oresult == STATE_CRITICAL) { 702 } else if (oresult == STATE_CRITICAL) {
656 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), offset); 703 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"),
704 ntp_res.offset);
657 } else { 705 } else {
658 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 706 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset);
659 } 707 }
660 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 708 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
661 709
662 if (do_jitter) { 710 if (config.do_jitter) {
663 if (jresult == STATE_WARNING) { 711 if (jresult == STATE_WARNING) {
664 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, jitter); 712 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter);
665 } else if (jresult == STATE_CRITICAL) { 713 } else if (jresult == STATE_CRITICAL) {
666 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, jitter); 714 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
667 } else { 715 } else {
668 xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); 716 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
669 } 717 }
670 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); 718 xasprintf(&perfdata_line, "%s %s", perfdata_line,
719 perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds));
671 } 720 }
672 if (do_stratum) { 721
722 if (config.do_stratum) {
673 if (sresult == STATE_WARNING) { 723 if (sresult == STATE_WARNING) {
674 xasprintf(&result_line, "%s, stratum=%i (WARNING)", result_line, stratum); 724 xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum);
675 } else if (sresult == STATE_CRITICAL) { 725 } else if (sresult == STATE_CRITICAL) {
676 xasprintf(&result_line, "%s, stratum=%i (CRITICAL)", result_line, stratum); 726 xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum);
677 } else { 727 } else {
678 xasprintf(&result_line, "%s, stratum=%i", result_line, stratum); 728 xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum);
679 } 729 }
680 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(stratum)); 730 xasprintf(&perfdata_line, "%s %s", perfdata_line,
731 perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds));
681 } 732 }
682 if (do_truechimers) { 733
734 if (config.do_truechimers) {
683 if (tresult == STATE_WARNING) { 735 if (tresult == STATE_WARNING) {
684 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, num_truechimers); 736 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line,
737 ntp_res.num_truechimers);
685 } else if (tresult == STATE_CRITICAL) { 738 } else if (tresult == STATE_CRITICAL) {
686 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, num_truechimers); 739 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line,
740 ntp_res.num_truechimers);
687 } else { 741 } else {
688 xasprintf(&result_line, "%s, truechimers=%i", result_line, num_truechimers); 742 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers);
689 } 743 }
690 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_truechimers(num_truechimers)); 744 xasprintf(&perfdata_line, "%s %s", perfdata_line,
745 perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers,
746 config.truechimer_thresholds));
691 } 747 }
748
692 printf("%s|%s\n", result_line, perfdata_line); 749 printf("%s|%s\n", result_line, perfdata_line);
693 750
694 if (server_address != NULL) 751 if (config.server_address != NULL) {
695 free(server_address); 752 free(config.server_address);
696 return result; 753 }
754
755 exit(result);
697} 756}
698 757
699void print_help(void) { 758void print_help(void) {
@@ -712,7 +771,8 @@ void print_help(void) {
712 printf(UT_IPv46); 771 printf(UT_IPv46);
713 printf(UT_HOST_PORT, 'p', "123"); 772 printf(UT_HOST_PORT, 'p', "123");
714 printf(" %s\n", "-q, --quiet"); 773 printf(" %s\n", "-q, --quiet");
715 printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); 774 printf(" %s\n",
775 _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized"));
716 printf(" %s\n", "-w, --warning=THRESHOLD"); 776 printf(" %s\n", "-w, --warning=THRESHOLD");
717 printf(" %s\n", _("Offset to result in warning status (seconds)")); 777 printf(" %s\n", _("Offset to result in warning status (seconds)"));
718 printf(" %s\n", "-c, --critical=THRESHOLD"); 778 printf(" %s\n", "-c, --critical=THRESHOLD");
@@ -749,7 +809,8 @@ void print_help(void) {
749 printf(" %s\n", _("Simple NTP server check:")); 809 printf(" %s\n", _("Simple NTP server check:"));
750 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); 810 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1"));
751 printf("\n"); 811 printf("\n");
752 printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); 812 printf(" %s\n",
813 _("Check jitter too, avoiding critical notifications if jitter isn't available"));
753 printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); 814 printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
754 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); 815 printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
755 printf("\n"); 816 printf("\n");
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h
new file mode 100644
index 00000000..00e6b05d
--- /dev/null
+++ b/plugins/check_ntp_peer.d/config.h
@@ -0,0 +1,67 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7enum {
8 DEFAULT_NTP_PORT = 123,
9};
10
11typedef struct {
12 char *server_address;
13 int port;
14
15 bool quiet;
16
17 // truechimer stuff
18 bool do_truechimers;
19 char *twarn;
20 char *tcrit;
21 thresholds *truechimer_thresholds;
22
23 char *owarn;
24 char *ocrit;
25 thresholds *offset_thresholds;
26
27 // stratum stuff
28 bool do_stratum;
29 char *swarn;
30 char *scrit;
31 thresholds *stratum_thresholds;
32
33 // jitter stuff
34 bool do_jitter;
35 char *jwarn;
36 char *jcrit;
37 thresholds *jitter_thresholds;
38
39} check_ntp_peer_config;
40
41check_ntp_peer_config check_ntp_peer_config_init() {
42 check_ntp_peer_config tmp = {
43 .server_address = NULL,
44 .port = DEFAULT_NTP_PORT,
45
46 .quiet = false,
47 .do_truechimers = false,
48 .twarn = "0:",
49 .tcrit = "0:",
50 .truechimer_thresholds = NULL,
51
52 .owarn = "60",
53 .ocrit = "120",
54 .offset_thresholds = NULL,
55
56 .do_stratum = false,
57 .swarn = "-1:16",
58 .scrit = "-1:16",
59 .stratum_thresholds = NULL,
60
61 .do_jitter = false,
62 .jwarn = "-1:5000",
63 .jcrit = "-1:10000",
64 .jitter_thresholds = NULL,
65 };
66 return tmp;
67}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index 703b69df..ad69b804 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -41,17 +41,18 @@ const char *email = "devel@monitoring-plugins.org";
41#include "common.h" 41#include "common.h"
42#include "netutils.h" 42#include "netutils.h"
43#include "utils.h" 43#include "utils.h"
44#include "states.h"
45#include "thresholds.h"
46#include "check_ntp_time.d/config.h"
44 47
45static char *server_address = NULL;
46static char *port = "123";
47static int verbose = 0; 48static int verbose = 0;
48static bool quiet = false;
49static char *owarn = "60";
50static char *ocrit = "120";
51static int time_offset = 0;
52 49
53static int process_arguments(int, char **); 50typedef struct {
54static thresholds *offset_thresholds = NULL; 51 int errorcode;
52 check_ntp_time_config config;
53} check_ntp_time_config_wrapper;
54static check_ntp_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
55
55static void print_help(void); 56static void print_help(void);
56void print_usage(void); 57void print_usage(void);
57 58
@@ -92,9 +93,9 @@ typedef struct {
92/* bits 1,2 are the leap indicator */ 93/* bits 1,2 are the leap indicator */
93#define LI_MASK 0xc0 94#define LI_MASK 0xc0
94#define LI(x) ((x & LI_MASK) >> 6) 95#define LI(x) ((x & LI_MASK) >> 6)
95#define LI_SET(x, y) \ 96#define LI_SET(x, y) \
96 do { \ 97 do { \
97 x |= ((y << 6) & LI_MASK); \ 98 x |= ((y << 6) & LI_MASK); \
98 } while (0) 99 } while (0)
99/* and these are the values of the leap indicator */ 100/* and these are the values of the leap indicator */
100#define LI_NOWARNING 0x00 101#define LI_NOWARNING 0x00
@@ -104,17 +105,17 @@ typedef struct {
104/* bits 3,4,5 are the ntp version */ 105/* bits 3,4,5 are the ntp version */
105#define VN_MASK 0x38 106#define VN_MASK 0x38
106#define VN(x) ((x & VN_MASK) >> 3) 107#define VN(x) ((x & VN_MASK) >> 3)
107#define VN_SET(x, y) \ 108#define VN_SET(x, y) \
108 do { \ 109 do { \
109 x |= ((y << 3) & VN_MASK); \ 110 x |= ((y << 3) & VN_MASK); \
110 } while (0) 111 } while (0)
111#define VN_RESERVED 0x02 112#define VN_RESERVED 0x02
112/* bits 6,7,8 are the ntp mode */ 113/* bits 6,7,8 are the ntp mode */
113#define MODE_MASK 0x07 114#define MODE_MASK 0x07
114#define MODE(x) (x & MODE_MASK) 115#define MODE(x) (x & MODE_MASK)
115#define MODE_SET(x, y) \ 116#define MODE_SET(x, y) \
116 do { \ 117 do { \
117 x |= (y & MODE_MASK); \ 118 x |= (y & MODE_MASK); \
118 } while (0) 119 } while (0)
119/* here are some values */ 120/* here are some values */
120#define MODE_CLIENT 0x03 121#define MODE_CLIENT 0x03
@@ -126,9 +127,9 @@ typedef struct {
126#define REM_MORE 0x20 127#define REM_MORE 0x20
127/* In control message, bits 11 - 15 are opcode */ 128/* In control message, bits 11 - 15 are opcode */
128#define OP_MASK 0x1f 129#define OP_MASK 0x1f
129#define OP_SET(x, y) \ 130#define OP_SET(x, y) \
130 do { \ 131 do { \
131 x |= (y & OP_MASK); \ 132 x |= (y & OP_MASK); \
132 } while (0) 133 } while (0)
133#define OP_READSTAT 0x01 134#define OP_READSTAT 0x01
134#define OP_READVAR 0x02 135#define OP_READVAR 0x02
@@ -159,35 +160,37 @@ typedef struct {
159#define EPOCHDIFF 0x83aa7e80UL 160#define EPOCHDIFF 0x83aa7e80UL
160 161
161/* extract a 32-bit ntp fixed point number into a double */ 162/* extract a 32-bit ntp fixed point number into a double */
162#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0) 163#define NTP32asDOUBLE(x) (ntohs(L16(x)) + ((double)ntohs(R16(x)) / 65536.0))
163 164
164/* likewise for a 64-bit ntp fp number */ 165/* likewise for a 64-bit ntp fp number */
165#define NTP64asDOUBLE(n) \ 166#define NTP64asDOUBLE(n) \
166 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) : 0) 167 (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \
168 (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \
169 : 0)
167 170
168/* convert a struct timeval to a double */ 171/* convert a struct timeval to a double */
169#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) 172#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
170 173
171/* convert an ntp 64-bit fp number to a struct timeval */ 174/* convert an ntp 64-bit fp number to a struct timeval */
172#define NTP64toTV(n, t) \ 175#define NTP64toTV(n, t) \
173 do { \ 176 do { \
174 if (!n) \ 177 if (!n) \
175 t.tv_sec = t.tv_usec = 0; \ 178 t.tv_sec = t.tv_usec = 0; \
176 else { \ 179 else { \
177 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ 180 t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \
178 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ 181 t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \
179 } \ 182 } \
180 } while (0) 183 } while (0)
181 184
182/* convert a struct timeval to an ntp 64-bit fp number */ 185/* convert a struct timeval to an ntp 64-bit fp number */
183#define TVtoNTP64(t, n) \ 186#define TVtoNTP64(t, n) \
184 do { \ 187 do { \
185 if (!t.tv_usec && !t.tv_sec) \ 188 if (!t.tv_usec && !t.tv_sec) \
186 n = 0x0UL; \ 189 n = 0x0UL; \
187 else { \ 190 else { \
188 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ 191 L32(n) = htonl(t.tv_sec + EPOCHDIFF); \
189 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ 192 R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \
190 } \ 193 } \
191 } while (0) 194 } while (0)
192 195
193/* NTP control message header is 12 bytes, plus any data in the data 196/* NTP control message header is 12 bytes, plus any data in the data
@@ -196,68 +199,64 @@ typedef struct {
196#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0)) 199#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0))
197 200
198/* finally, a little helper or two for debugging: */ 201/* finally, a little helper or two for debugging: */
199#define DBG(x) \ 202#define DBG(x) \
200 do { \ 203 do { \
201 if (verbose > 1) { \ 204 if (verbose > 1) { \
202 x; \ 205 x; \
203 } \ 206 } \
204 } while (0); 207 } while (0);
205#define PRINTSOCKADDR(x) \ 208#define PRINTSOCKADDR(x) \
206 do { \ 209 do { \
207 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ 210 printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \
208 } while (0); 211 } while (0);
209 212
210/* calculate the offset of the local clock */ 213/* calculate the offset of the local clock */
211static inline double calc_offset(const ntp_message *m, const struct timeval *t) { 214static inline double calc_offset(const ntp_message *message, const struct timeval *time_value) {
212 double client_tx = NTP64asDOUBLE(m->origts); 215 double client_tx = NTP64asDOUBLE(message->origts);
213 double peer_rx = NTP64asDOUBLE(m->rxts); 216 double peer_rx = NTP64asDOUBLE(message->rxts);
214 double peer_tx = NTP64asDOUBLE(m->txts); 217 double peer_tx = NTP64asDOUBLE(message->txts);
215 double client_rx = TVasDOUBLE((*t)); 218 double client_rx = TVasDOUBLE((*time_value));
216 return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx))); 219 return (((peer_tx - client_rx) + (peer_rx - client_tx)) / 2);
217} 220}
218 221
219/* print out a ntp packet in human readable/debuggable format */ 222/* print out a ntp packet in human readable/debuggable format */
220void print_ntp_message(const ntp_message *p) { 223void print_ntp_message(const ntp_message *message) {
221 struct timeval ref; 224 struct timeval ref;
222 struct timeval orig; 225 struct timeval orig;
223 struct timeval rx;
224 struct timeval tx;
225 226
226 NTP64toTV(p->refts, ref); 227 NTP64toTV(message->refts, ref);
227 NTP64toTV(p->origts, orig); 228 NTP64toTV(message->origts, orig);
228 NTP64toTV(p->rxts, rx);
229 NTP64toTV(p->txts, tx);
230 229
231 printf("packet contents:\n"); 230 printf("packet contents:\n");
232 printf("\tflags: 0x%.2x\n", p->flags); 231 printf("\tflags: 0x%.2x\n", message->flags);
233 printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); 232 printf("\t li=%d (0x%.2x)\n", LI(message->flags), message->flags & LI_MASK);
234 printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); 233 printf("\t vn=%d (0x%.2x)\n", VN(message->flags), message->flags & VN_MASK);
235 printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); 234 printf("\t mode=%d (0x%.2x)\n", MODE(message->flags), message->flags & MODE_MASK);
236 printf("\tstratum = %d\n", p->stratum); 235 printf("\tstratum = %d\n", message->stratum);
237 printf("\tpoll = %g\n", pow(2, p->poll)); 236 printf("\tpoll = %g\n", pow(2, message->poll));
238 printf("\tprecision = %g\n", pow(2, p->precision)); 237 printf("\tprecision = %g\n", pow(2, message->precision));
239 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay)); 238 printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(message->rtdelay));
240 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp)); 239 printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(message->rtdisp));
241 printf("\trefid = %x\n", p->refid); 240 printf("\trefid = %x\n", message->refid);
242 printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts)); 241 printf("\trefts = %-.16g\n", NTP64asDOUBLE(message->refts));
243 printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts)); 242 printf("\torigts = %-.16g\n", NTP64asDOUBLE(message->origts));
244 printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts)); 243 printf("\trxts = %-.16g\n", NTP64asDOUBLE(message->rxts));
245 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); 244 printf("\ttxts = %-.16g\n", NTP64asDOUBLE(message->txts));
246} 245}
247 246
248void setup_request(ntp_message *p) { 247void setup_request(ntp_message *message) {
249 memset(p, 0, sizeof(ntp_message)); 248 memset(message, 0, sizeof(ntp_message));
250 LI_SET(p->flags, LI_ALARM); 249 LI_SET(message->flags, LI_ALARM);
251 VN_SET(p->flags, 4); 250 VN_SET(message->flags, 4);
252 MODE_SET(p->flags, MODE_CLIENT); 251 MODE_SET(message->flags, MODE_CLIENT);
253 p->poll = 4; 252 message->poll = 4;
254 p->precision = (int8_t)0xfa; 253 message->precision = (int8_t)0xfa;
255 L16(p->rtdelay) = htons(1); 254 L16(message->rtdelay) = htons(1);
256 L16(p->rtdisp) = htons(1); 255 L16(message->rtdisp) = htons(1);
257 256
258 struct timeval t; 257 struct timeval t;
259 gettimeofday(&t, NULL); 258 gettimeofday(&t, NULL);
260 TVtoNTP64(t, p->txts); 259 TVtoNTP64(t, message->txts);
261} 260}
262 261
263/* select the "best" server from a list of servers, and return its index. 262/* select the "best" server from a list of servers, and return its index.
@@ -273,14 +272,16 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
273 * stratum 0 is for reference clocks so no NTP server should ever report 272 * stratum 0 is for reference clocks so no NTP server should ever report
274 * a stratum 0 */ 273 * a stratum 0 */
275 if (slist[cserver].stratum == 0) { 274 if (slist[cserver].stratum == 0) {
276 if (verbose) 275 if (verbose) {
277 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum); 276 printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
277 }
278 continue; 278 continue;
279 } 279 }
280 /* Sort out servers with error flags */ 280 /* Sort out servers with error flags */
281 if (LI(slist[cserver].flags) == LI_ALARM) { 281 if (LI(slist[cserver].flags) == LI_ALARM) {
282 if (verbose) 282 if (verbose) {
283 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags)); 283 printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
284 }
284 continue; 285 continue;
285 } 286 }
286 287
@@ -322,7 +323,7 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
322 * we don't waste time sitting around waiting for single packets. 323 * we don't waste time sitting around waiting for single packets.
323 * - we also "manually" handle resolving host names and connecting, because 324 * - we also "manually" handle resolving host names and connecting, because
324 * we have to do it in a way that our lazy macros don't handle currently :( */ 325 * we have to do it in a way that our lazy macros don't handle currently :( */
325double offset_request(const char *host, int *status) { 326double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) {
326 /* setup hints to only return results from getaddrinfo that we'd like */ 327 /* setup hints to only return results from getaddrinfo that we'd like */
327 struct addrinfo hints; 328 struct addrinfo hints;
328 memset(&hints, 0, sizeof(struct addrinfo)); 329 memset(&hints, 0, sizeof(struct addrinfo));
@@ -331,39 +332,44 @@ double offset_request(const char *host, int *status) {
331 hints.ai_socktype = SOCK_DGRAM; 332 hints.ai_socktype = SOCK_DGRAM;
332 333
333 /* fill in ai with the list of hosts resolved by the host name */ 334 /* fill in ai with the list of hosts resolved by the host name */
334 struct addrinfo *ai = NULL; 335 struct addrinfo *addresses = NULL;
335 int ga_result = getaddrinfo(host, port, &hints, &ai); 336 int ga_result = getaddrinfo(host, port, &hints, &addresses);
336 if (ga_result != 0) { 337 if (ga_result != 0) {
337 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); 338 die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result));
338 } 339 }
339 340
340 /* count the number of returned hosts, and allocate stuff accordingly */ 341 /* count the number of returned hosts, and allocate stuff accordingly */
341 int num_hosts = 0; 342 size_t num_hosts = 0;
342 for (struct addrinfo *ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { 343 for (struct addrinfo *ai_tmp = addresses; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
343 num_hosts++; 344 num_hosts++;
344 } 345 }
345 346
346 ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts); 347 ntp_message *req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts);
347 348
348 if (req == NULL) 349 if (req == NULL) {
349 die(STATE_UNKNOWN, "can not allocate ntp message array"); 350 die(STATE_UNKNOWN, "can not allocate ntp message array");
351 }
350 int *socklist = (int *)malloc(sizeof(int) * num_hosts); 352 int *socklist = (int *)malloc(sizeof(int) * num_hosts);
351 353
352 if (socklist == NULL) 354 if (socklist == NULL) {
353 die(STATE_UNKNOWN, "can not allocate socket array"); 355 die(STATE_UNKNOWN, "can not allocate socket array");
356 }
354 357
355 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts); 358 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts);
356 if (ufds == NULL) 359 if (ufds == NULL) {
357 die(STATE_UNKNOWN, "can not allocate socket array"); 360 die(STATE_UNKNOWN, "can not allocate socket array");
361 }
358 362
359 ntp_server_results *servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); 363 ntp_server_results *servers =
360 if (servers == NULL) 364 (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts);
365 if (servers == NULL) {
361 die(STATE_UNKNOWN, "can not allocate server array"); 366 die(STATE_UNKNOWN, "can not allocate server array");
367 }
362 memset(servers, 0, sizeof(ntp_server_results) * num_hosts); 368 memset(servers, 0, sizeof(ntp_server_results) * num_hosts);
363 DBG(printf("Found %d peers to check\n", num_hosts)); 369 DBG(printf("Found %zu peers to check\n", num_hosts));
364 370
365 /* setup each socket for writing, and the corresponding struct pollfd */ 371 /* setup each socket for writing, and the corresponding struct pollfd */
366 struct addrinfo *ai_tmp = ai; 372 struct addrinfo *ai_tmp = addresses;
367 for (int i = 0; ai_tmp; i++) { 373 for (int i = 0; ai_tmp; i++) {
368 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); 374 socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
369 if (socklist[i] == -1) { 375 if (socklist[i] == -1) {
@@ -389,7 +395,7 @@ double offset_request(const char *host, int *status) {
389 time_t start_ts = 0; 395 time_t start_ts = 0;
390 time_t now_time = 0; 396 time_t now_time = 0;
391 now_time = start_ts = time(NULL); 397 now_time = start_ts = time(NULL);
392 int servers_completed = 0; 398 size_t servers_completed = 0;
393 bool one_read = false; 399 bool one_read = false;
394 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) { 400 while (servers_completed < num_hosts && now_time - start_ts <= socket_timeout / 2) {
395 /* loop through each server and find each one which hasn't 401 /* loop through each server and find each one which hasn't
@@ -398,12 +404,14 @@ double offset_request(const char *host, int *status) {
398 * and update the "waiting" timestamp with the current time. */ 404 * and update the "waiting" timestamp with the current time. */
399 now_time = time(NULL); 405 now_time = time(NULL);
400 406
401 for (int i = 0; i < num_hosts; i++) { 407 for (size_t i = 0; i < num_hosts; i++) {
402 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) { 408 if (servers[i].waiting < now_time && servers[i].num_responses < AVG_NUM) {
403 if (verbose && servers[i].waiting != 0) 409 if (verbose && servers[i].waiting != 0) {
404 printf("re-"); 410 printf("re-");
405 if (verbose) 411 }
406 printf("sending request to peer %d\n", i); 412 if (verbose) {
413 printf("sending request to peer %zu\n", i);
414 }
407 setup_request(&req[i]); 415 setup_request(&req[i]);
408 write(socklist[i], &req[i], sizeof(ntp_message)); 416 write(socklist[i], &req[i], sizeof(ntp_message));
409 servers[i].waiting = now_time; 417 servers[i].waiting = now_time;
@@ -419,10 +427,10 @@ double offset_request(const char *host, int *status) {
419 } 427 }
420 428
421 /* read from any sockets with pending data */ 429 /* read from any sockets with pending data */
422 for (int i = 0; servers_readable && i < num_hosts; i++) { 430 for (size_t i = 0; servers_readable && i < num_hosts; i++) {
423 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) { 431 if (ufds[i].revents & POLLIN && servers[i].num_responses < AVG_NUM) {
424 if (verbose) { 432 if (verbose) {
425 printf("response from peer %d: ", i); 433 printf("response from peer %zu: ", i);
426 } 434 }
427 435
428 read(ufds[i].fd, &req[i], sizeof(ntp_message)); 436 read(ufds[i].fd, &req[i], sizeof(ntp_message));
@@ -442,14 +450,15 @@ double offset_request(const char *host, int *status) {
442 servers[i].flags = req[i].flags; 450 servers[i].flags = req[i].flags;
443 servers_readable--; 451 servers_readable--;
444 one_read = true; 452 one_read = true;
445 if (servers[i].num_responses == AVG_NUM) 453 if (servers[i].num_responses == AVG_NUM) {
446 servers_completed++; 454 servers_completed++;
455 }
447 } 456 }
448 } 457 }
449 /* lather, rinse, repeat. */ 458 /* lather, rinse, repeat. */
450 } 459 }
451 460
452 if (one_read == false) { 461 if (!one_read) {
453 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 462 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
454 } 463 }
455 464
@@ -467,21 +476,22 @@ double offset_request(const char *host, int *status) {
467 } 476 }
468 477
469 /* cleanup */ 478 /* cleanup */
470 for (int j = 0; j < num_hosts; j++) { 479 for (size_t j = 0; j < num_hosts; j++) {
471 close(socklist[j]); 480 close(socklist[j]);
472 } 481 }
473 free(socklist); 482 free(socklist);
474 free(ufds); 483 free(ufds);
475 free(servers); 484 free(servers);
476 free(req); 485 free(req);
477 freeaddrinfo(ai); 486 freeaddrinfo(addresses);
478 487
479 if (verbose) 488 if (verbose) {
480 printf("overall average offset: %.10g\n", avg_offset); 489 printf("overall average offset: %.10g\n", avg_offset);
490 }
481 return avg_offset; 491 return avg_offset;
482} 492}
483 493
484int process_arguments(int argc, char **argv) { 494check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
485 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 495 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
486 {"help", no_argument, 0, 'h'}, 496 {"help", no_argument, 0, 'h'},
487 {"verbose", no_argument, 0, 'v'}, 497 {"verbose", no_argument, 0, 'v'},
@@ -496,14 +506,24 @@ int process_arguments(int argc, char **argv) {
496 {"port", required_argument, 0, 'p'}, 506 {"port", required_argument, 0, 'p'},
497 {0, 0, 0, 0}}; 507 {0, 0, 0, 0}};
498 508
499 if (argc < 2) 509 if (argc < 2) {
500 usage("\n"); 510 usage("\n");
511 }
512
513 check_ntp_time_config_wrapper result = {
514 .errorcode = OK,
515 .config = check_ntp_time_config_init(),
516 };
517
518 char *owarn = "60";
519 char *ocrit = "120";
501 520
502 while (true) { 521 while (true) {
503 int option = 0; 522 int option = 0;
504 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 523 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
505 if (option_char == -1 || option_char == EOF || option_char == 1) 524 if (option_char == -1 || option_char == EOF || option_char == 1) {
506 break; 525 break;
526 }
507 527
508 switch (option_char) { 528 switch (option_char) {
509 case 'h': 529 case 'h':
@@ -518,7 +538,7 @@ int process_arguments(int argc, char **argv) {
518 verbose++; 538 verbose++;
519 break; 539 break;
520 case 'q': 540 case 'q':
521 quiet = true; 541 result.config.quiet = true;
522 break; 542 break;
523 case 'w': 543 case 'w':
524 owarn = optarg; 544 owarn = optarg;
@@ -527,18 +547,19 @@ int process_arguments(int argc, char **argv) {
527 ocrit = optarg; 547 ocrit = optarg;
528 break; 548 break;
529 case 'H': 549 case 'H':
530 if (!is_host(optarg)) 550 if (!is_host(optarg)) {
531 usage2(_("Invalid hostname/address"), optarg); 551 usage2(_("Invalid hostname/address"), optarg);
532 server_address = strdup(optarg); 552 }
553 result.config.server_address = strdup(optarg);
533 break; 554 break;
534 case 'p': 555 case 'p':
535 port = strdup(optarg); 556 result.config.port = strdup(optarg);
536 break; 557 break;
537 case 't': 558 case 't':
538 socket_timeout = atoi(optarg); 559 socket_timeout = atoi(optarg);
539 break; 560 break;
540 case 'o': 561 case 'o':
541 time_offset = atoi(optarg); 562 result.config.time_offset = atoi(optarg);
542 break; 563 break;
543 case '4': 564 case '4':
544 address_family = AF_INET; 565 address_family = AF_INET;
@@ -557,16 +578,18 @@ int process_arguments(int argc, char **argv) {
557 } 578 }
558 } 579 }
559 580
560 if (server_address == NULL) { 581 if (result.config.server_address == NULL) {
561 usage4(_("Hostname was not supplied")); 582 usage4(_("Hostname was not supplied"));
562 } 583 }
563 584
564 return 0; 585 set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
586
587 return result;
565} 588}
566 589
567char *perfd_offset(double offset) { 590char *perfd_offset(double offset, thresholds *offset_thresholds) {
568 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, 591 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
569 0); 592 offset_thresholds->critical->end, false, 0, false, 0);
570} 593}
571 594
572int main(int argc, char *argv[]) { 595int main(int argc, char *argv[]) {
@@ -577,10 +600,13 @@ int main(int argc, char *argv[]) {
577 /* Parse extra opts if any */ 600 /* Parse extra opts if any */
578 argv = np_extra_opts(&argc, argv, progname); 601 argv = np_extra_opts(&argc, argv, progname);
579 602
580 if (process_arguments(argc, argv) == ERROR) 603 check_ntp_time_config_wrapper tmp_config = process_arguments(argc, argv);
604
605 if (tmp_config.errorcode == ERROR) {
581 usage4(_("Could not parse arguments")); 606 usage4(_("Could not parse arguments"));
607 }
582 608
583 set_thresholds(&offset_thresholds, owarn, ocrit); 609 const check_ntp_time_config config = tmp_config.config;
584 610
585 /* initialize alarm signal handling */ 611 /* initialize alarm signal handling */
586 signal(SIGALRM, socket_timeout_alarm_handler); 612 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -588,13 +614,14 @@ int main(int argc, char *argv[]) {
588 /* set socket timeout */ 614 /* set socket timeout */
589 alarm(socket_timeout); 615 alarm(socket_timeout);
590 616
591 int offset_result = STATE_OK; 617 mp_state_enum offset_result = STATE_OK;
592 int result = STATE_OK; 618 mp_state_enum result = STATE_OK;
593 double offset = offset_request(server_address, &offset_result); 619 double offset =
620 offset_request(config.server_address, config.port, &offset_result, config.time_offset);
594 if (offset_result == STATE_UNKNOWN) { 621 if (offset_result == STATE_UNKNOWN) {
595 result = ((!quiet) ? STATE_UNKNOWN : STATE_CRITICAL); 622 result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
596 } else { 623 } else {
597 result = get_status(fabs(offset), offset_thresholds); 624 result = get_status(fabs(offset), config.offset_thresholds);
598 } 625 }
599 626
600 char *result_line; 627 char *result_line;
@@ -619,13 +646,14 @@ int main(int argc, char *argv[]) {
619 xasprintf(&perfdata_line, ""); 646 xasprintf(&perfdata_line, "");
620 } else { 647 } else {
621 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 648 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
622 xasprintf(&perfdata_line, "%s", perfd_offset(offset)); 649 xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
623 } 650 }
624 printf("%s|%s\n", result_line, perfdata_line); 651 printf("%s|%s\n", result_line, perfdata_line);
625 652
626 if (server_address != NULL) 653 if (config.server_address != NULL) {
627 free(server_address); 654 free(config.server_address);
628 return result; 655 }
656 exit(result);
629} 657}
630 658
631void print_help(void) { 659void print_help(void) {
@@ -677,5 +705,6 @@ void print_help(void) {
677 705
678void print_usage(void) { 706void print_usage(void) {
679 printf("%s\n", _("Usage:")); 707 printf("%s\n", _("Usage:"));
680 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n", progname); 708 printf(" %s -H <host> [-4|-6] [-w <warn>] [-c <crit>] [-v verbose] [-o <time offset>]\n",
709 progname);
681} 710}
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
new file mode 100644
index 00000000..99dabbbd
--- /dev/null
+++ b/plugins/check_ntp_time.d/config.h
@@ -0,0 +1,28 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6
7typedef struct {
8 char *server_address;
9 char *port;
10
11 bool quiet;
12 int time_offset;
13
14 thresholds *offset_thresholds;
15} check_ntp_time_config;
16
17check_ntp_time_config check_ntp_time_config_init() {
18 check_ntp_time_config tmp = {
19 .server_address = NULL,
20 .port = "123",
21
22 .quiet = false,
23 .time_offset = 0,
24
25 .offset_thresholds = NULL,
26 };
27 return tmp;
28}
diff --git a/plugins/check_nwstat.c b/plugins/check_nwstat.c
deleted file mode 100644
index 176dfbc8..00000000
--- a/plugins/check_nwstat.c
+++ /dev/null
@@ -1,1527 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_nwstat plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_nwstat plugin
11 *
12 * This plugin attempts to contact the MRTGEXT NLM running on a
13 * Novell server to gather the requested system information.
14 *
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 *
29 *
30 *****************************************************************************/
31
32const char *progname = "check_nwstat";
33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1, /* check 1 minute CPU load */
43 LOAD5, /* check 5 minute CPU load */
44 LOAD15, /* check 15 minute CPU load */
45 CONNS, /* check number of connections */
46 VPF, /* check % free space on volume */
47 VMF, /* check MB free space on volume */
48 VMU, /* check MB used space on volume */
49 VPU, /* check % used space on volume */
50 VMP, /* check MB purgeable space on volume */
51 VKF, /* check KB free space on volume */
52 LTCH, /* check long-term cache hit percentage */
53 CBUFF, /* check total cache buffers */
54 CDBUFF, /* check dirty cache buffers */
55 LRUM, /* check LRU sitting time in minutes */
56 DSDB, /* check to see if DS Database is open */
57 LOGINS, /* check to see if logins are enabled */
58 NRMH, /* check to see NRM Health Status */
59 PUPRB, /* check % of used packet receive buffers */
60 UPRB, /* check used packet receive buffers */
61 SAPENTRIES, /* check SAP entries */
62 OFILES, /* check number of open files */
63 VKP, /* check KB purgeable space on volume */
64 VPP, /* check % purgeable space on volume */
65 VKNP, /* check KB not yet purgeable space on volume */
66 VPNP, /* check % not yet purgeable space on volume */
67 ABENDS, /* check abended thread count */
68 CSPROCS, /* check number of current service processes */
69 TSYNC, /* check timesync status 0=no 1=yes in sync to the network */
70 LRUS, /* check LRU sitting time in seconds */
71 DCB, /* check dirty cache buffers as a percentage of the total */
72 TCB, /* check total cache buffers as a percentage of the original */
73 DSVER, /* check NDS version */
74 UPTIME, /* check server uptime */
75 NLM, /* check NLM loaded */
76 NRMP, /* check NRM Process Values */
77 NRMM, /* check NRM Memory Values */
78 NRMS, /* check NRM Values */
79 NSS1, /* check Statistics from _Admin:Manage_NSS\GeneralStats.xml */
80 NSS2, /* check Statistics from _Admin:Manage_NSS\BufferCache.xml */
81 NSS3, /* check statistics from _Admin:Manage_NSS\NameCache.xml */
82 NSS4, /* check statistics from _Admin:Manage_NSS\FileStats.xml */
83 NSS5, /* check statistics from _Admin:Manage_NSS\ObjectCache.xml */
84 NSS6, /* check statistics from _Admin:Manage_NSS\Thread.xml */
85 NSS7 /* check statistics from _Admin:Manage_NSS\AuthorizationCache.xml */
86};
87
88enum {
89 PORT = 9999
90};
91
92static char *server_address = NULL;
93static char *volume_name = NULL;
94static char *nlm_name = NULL;
95static char *nrmp_name = NULL;
96static char *nrmm_name = NULL;
97static char *nrms_name = NULL;
98static char *nss1_name = NULL;
99static char *nss2_name = NULL;
100static char *nss3_name = NULL;
101static char *nss4_name = NULL;
102static char *nss5_name = NULL;
103static char *nss6_name = NULL;
104static char *nss7_name = NULL;
105static int server_port = PORT;
106static unsigned long warning_value = 0L;
107static unsigned long critical_value = 0L;
108static bool check_warning_value = false;
109static bool check_critical_value = false;
110static bool check_netware_version = false;
111static enum checkvar vars_to_check = NONE;
112static int sap_number = -1;
113
114static int process_arguments(int /*argc*/, char ** /*argv*/);
115static void print_help(void);
116void print_usage(void);
117
118int main(int argc, char **argv) {
119 int result = STATE_UNKNOWN;
120 int sd;
121 char *send_buffer = NULL;
122 char recv_buffer[MAX_INPUT_BUFFER];
123 char *output_message = NULL;
124 char *temp_buffer = NULL;
125 char *netware_version = NULL;
126
127 int time_sync_status = 0;
128 int nrm_health_status = 0;
129 unsigned long total_cache_buffers = 0;
130 unsigned long dirty_cache_buffers = 0;
131 unsigned long open_files = 0;
132 unsigned long abended_threads = 0;
133 unsigned long max_service_processes = 0;
134 unsigned long current_service_processes = 0;
135 unsigned long free_disk_space = 0L;
136 unsigned long nrmp_value = 0L;
137 unsigned long nrmm_value = 0L;
138 unsigned long nrms_value = 0L;
139 unsigned long nss1_value = 0L;
140 unsigned long nss2_value = 0L;
141 unsigned long nss3_value = 0L;
142 unsigned long nss4_value = 0L;
143 unsigned long nss5_value = 0L;
144 unsigned long nss6_value = 0L;
145 unsigned long nss7_value = 0L;
146 unsigned long total_disk_space = 0L;
147 unsigned long used_disk_space = 0L;
148 unsigned long percent_used_disk_space = 0L;
149 unsigned long purgeable_disk_space = 0L;
150 unsigned long non_purgeable_disk_space = 0L;
151 unsigned long percent_free_space = 0;
152 unsigned long percent_purgeable_space = 0;
153 unsigned long percent_non_purgeable_space = 0;
154 unsigned long current_connections = 0L;
155 unsigned long utilization = 0L;
156 unsigned long cache_hits = 0;
157 unsigned long cache_buffers = 0L;
158 unsigned long lru_time = 0L;
159 unsigned long max_packet_receive_buffers = 0;
160 unsigned long used_packet_receive_buffers = 0;
161 unsigned long percent_used_packet_receive_buffers = 0L;
162 unsigned long sap_entries = 0;
163 char uptime[MAX_INPUT_BUFFER];
164
165 setlocale(LC_ALL, "");
166 bindtextdomain(PACKAGE, LOCALEDIR);
167 textdomain(PACKAGE);
168
169 /* Parse extra opts if any */
170 argv = np_extra_opts(&argc, argv, progname);
171
172 if (process_arguments(argc, argv) == ERROR)
173 usage4(_("Could not parse arguments"));
174
175 /* initialize alarm signal handling */
176 signal(SIGALRM, socket_timeout_alarm_handler);
177
178 /* set socket timeout */
179 alarm(socket_timeout);
180
181 /* open connection */
182 my_tcp_connect(server_address, server_port, &sd);
183
184 /* get OS version string */
185 if (check_netware_version) {
186 send_buffer = strdup("S19\r\n");
187 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
188 if (result != STATE_OK)
189 return result;
190 if (!strcmp(recv_buffer, "-1\n"))
191 netware_version = strdup("");
192 else {
193 recv_buffer[strlen(recv_buffer) - 1] = 0;
194 xasprintf(&netware_version, _("NetWare %s: "), recv_buffer);
195 }
196 } else
197 netware_version = strdup("");
198
199 /* check CPU load */
200 if (vars_to_check == LOAD1 || vars_to_check == LOAD5 || vars_to_check == LOAD15) {
201
202 switch (vars_to_check) {
203 case LOAD1:
204 temp_buffer = strdup("1");
205 break;
206 case LOAD5:
207 temp_buffer = strdup("5");
208 break;
209 default:
210 temp_buffer = strdup("15");
211 break;
212 }
213
214 close(sd);
215 my_tcp_connect(server_address, server_port, &sd);
216
217 xasprintf(&send_buffer, "UTIL%s\r\n", temp_buffer);
218 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
219 if (result != STATE_OK)
220 return result;
221 utilization = strtoul(recv_buffer, NULL, 10);
222
223 close(sd);
224 my_tcp_connect(server_address, server_port, &sd);
225
226 send_buffer = strdup("UPTIME\r\n");
227 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
228 if (result != STATE_OK)
229 return result;
230 recv_buffer[strlen(recv_buffer) - 1] = 0;
231 sprintf(uptime, _("Up %s,"), recv_buffer);
232
233 if (check_critical_value && utilization >= critical_value)
234 result = STATE_CRITICAL;
235 else if (check_warning_value && utilization >= warning_value)
236 result = STATE_WARNING;
237
238 xasprintf(&output_message, _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"), state_text(result), uptime,
239 temp_buffer, utilization, temp_buffer, utilization, warning_value, critical_value);
240
241 /* check number of user connections */
242 } else if (vars_to_check == CONNS) {
243
244 close(sd);
245 my_tcp_connect(server_address, server_port, &sd);
246
247 send_buffer = strdup("CONNECT\r\n");
248 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
249 if (result != STATE_OK)
250 return result;
251 current_connections = strtoul(recv_buffer, NULL, 10);
252
253 if (check_critical_value && current_connections >= critical_value)
254 result = STATE_CRITICAL;
255 else if (check_warning_value && current_connections >= warning_value)
256 result = STATE_WARNING;
257
258 xasprintf(&output_message, _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"), state_text(result), current_connections,
259 current_connections, warning_value, critical_value);
260
261 /* check % long term cache hits */
262 } else if (vars_to_check == LTCH) {
263
264 close(sd);
265 my_tcp_connect(server_address, server_port, &sd);
266
267 send_buffer = strdup("S1\r\n");
268 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
269 if (result != STATE_OK)
270 return result;
271 cache_hits = atoi(recv_buffer);
272
273 if (check_critical_value && cache_hits <= critical_value)
274 result = STATE_CRITICAL;
275 else if (check_warning_value && cache_hits <= warning_value)
276 result = STATE_WARNING;
277
278 xasprintf(&output_message, _("%s: Long term cache hits = %lu%%"), state_text(result), cache_hits);
279
280 /* check cache buffers */
281 } else if (vars_to_check == CBUFF) {
282
283 close(sd);
284 my_tcp_connect(server_address, server_port, &sd);
285
286 send_buffer = strdup("S2\r\n");
287 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
288 if (result != STATE_OK)
289 return result;
290 cache_buffers = strtoul(recv_buffer, NULL, 10);
291
292 if (check_critical_value && cache_buffers <= critical_value)
293 result = STATE_CRITICAL;
294 else if (check_warning_value && cache_buffers <= warning_value)
295 result = STATE_WARNING;
296
297 xasprintf(&output_message, _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers,
298 cache_buffers, warning_value, critical_value);
299
300 /* check dirty cache buffers */
301 } else if (vars_to_check == CDBUFF) {
302
303 close(sd);
304 my_tcp_connect(server_address, server_port, &sd);
305
306 send_buffer = strdup("S3\r\n");
307 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
308 if (result != STATE_OK)
309 return result;
310 cache_buffers = strtoul(recv_buffer, NULL, 10);
311
312 if (check_critical_value && cache_buffers >= critical_value)
313 result = STATE_CRITICAL;
314 else if (check_warning_value && cache_buffers >= warning_value)
315 result = STATE_WARNING;
316
317 xasprintf(&output_message, _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers,
318 cache_buffers, warning_value, critical_value);
319
320 /* check LRU sitting time in minutes */
321 } else if (vars_to_check == LRUM) {
322
323 close(sd);
324 my_tcp_connect(server_address, server_port, &sd);
325
326 send_buffer = strdup("S5\r\n");
327 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
328 if (result != STATE_OK)
329 return result;
330 lru_time = strtoul(recv_buffer, NULL, 10);
331
332 if (check_critical_value && lru_time <= critical_value)
333 result = STATE_CRITICAL;
334 else if (check_warning_value && lru_time <= warning_value)
335 result = STATE_WARNING;
336
337 xasprintf(&output_message, _("%s: LRU sitting time = %lu minutes"), state_text(result), lru_time);
338
339 /* check KB free space on volume */
340 } else if (vars_to_check == VKF) {
341
342 close(sd);
343 my_tcp_connect(server_address, server_port, &sd);
344
345 xasprintf(&send_buffer, "VKF%s\r\n", volume_name);
346 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
347 if (result != STATE_OK)
348 return result;
349
350 if (!strcmp(recv_buffer, "-1\n")) {
351 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
352 result = STATE_CRITICAL;
353 } else {
354 free_disk_space = strtoul(recv_buffer, NULL, 10);
355 if (check_critical_value && free_disk_space <= critical_value)
356 result = STATE_CRITICAL;
357 else if (check_warning_value && free_disk_space <= warning_value)
358 result = STATE_WARNING;
359 xasprintf(&output_message, _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
360 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
361 }
362
363 /* check MB free space on volume */
364 } else if (vars_to_check == VMF) {
365
366 xasprintf(&send_buffer, "VMF%s\r\n", volume_name);
367 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
368 if (result != STATE_OK)
369 return result;
370
371 if (!strcmp(recv_buffer, "-1\n")) {
372 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
373 result = STATE_CRITICAL;
374 } else {
375 free_disk_space = strtoul(recv_buffer, NULL, 10);
376 if (check_critical_value && free_disk_space <= critical_value)
377 result = STATE_CRITICAL;
378 else if (check_warning_value && free_disk_space <= warning_value)
379 result = STATE_WARNING;
380 xasprintf(&output_message, _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
381 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
382 }
383 /* check MB used space on volume */
384 } else if (vars_to_check == VMU) {
385
386 xasprintf(&send_buffer, "VMU%s\r\n", volume_name);
387 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
388 if (result != STATE_OK)
389 return result;
390
391 if (!strcmp(recv_buffer, "-1\n")) {
392 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
393 result = STATE_CRITICAL;
394 } else {
395 free_disk_space = strtoul(recv_buffer, NULL, 10);
396 if (check_critical_value && free_disk_space <= critical_value)
397 result = STATE_CRITICAL;
398 else if (check_warning_value && free_disk_space <= warning_value)
399 result = STATE_WARNING;
400 xasprintf(&output_message, _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
401 free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value);
402 }
403 /* check % used space on volume */
404 } else if (vars_to_check == VPU) {
405 close(sd);
406 my_tcp_connect(server_address, server_port, &sd);
407
408 asprintf(&send_buffer, "VMU%s\r\n", volume_name);
409 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
410
411 if (result != STATE_OK)
412 return result;
413
414 if (!strcmp(recv_buffer, "-1\n")) {
415 asprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
416 result = STATE_CRITICAL;
417
418 } else {
419 used_disk_space = strtoul(recv_buffer, NULL, 10);
420 close(sd);
421 my_tcp_connect(server_address, server_port, &sd);
422 /* get total volume in MB */
423 asprintf(&send_buffer, "VMS%s\r\n", volume_name);
424 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
425 if (result != STATE_OK)
426 return result;
427 total_disk_space = strtoul(recv_buffer, NULL, 10);
428 /* calculate percent used on volume */
429 percent_used_disk_space = (unsigned long)(((double)used_disk_space / (double)total_disk_space) * 100.0);
430
431 if (check_critical_value && percent_used_disk_space >= critical_value)
432 result = STATE_CRITICAL;
433 else if (check_warning_value && percent_used_disk_space >= warning_value)
434 result = STATE_WARNING;
435
436 asprintf(&output_message, _("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"),
437 used_disk_space, percent_used_disk_space, volume_name, total_disk_space, volume_name, percent_used_disk_space,
438 warning_value, critical_value);
439 }
440
441 /* check % free space on volume */
442 } else if (vars_to_check == VPF) {
443
444 close(sd);
445 my_tcp_connect(server_address, server_port, &sd);
446
447 xasprintf(&send_buffer, "VKF%s\r\n", volume_name);
448 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
449 if (result != STATE_OK)
450 return result;
451
452 if (!strcmp(recv_buffer, "-1\n")) {
453
454 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
455 result = STATE_CRITICAL;
456
457 } else {
458
459 free_disk_space = strtoul(recv_buffer, NULL, 10);
460
461 close(sd);
462 my_tcp_connect(server_address, server_port, &sd);
463
464 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
465 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
466 if (result != STATE_OK)
467 return result;
468 total_disk_space = strtoul(recv_buffer, NULL, 10);
469
470 percent_free_space = (unsigned long)(((double)free_disk_space / (double)total_disk_space) * 100.0);
471
472 if (check_critical_value && percent_free_space <= critical_value)
473 result = STATE_CRITICAL;
474 else if (check_warning_value && percent_free_space <= warning_value)
475 result = STATE_WARNING;
476 free_disk_space /= 1024;
477 total_disk_space /= 1024;
478 xasprintf(&output_message, _("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"), free_disk_space,
479 percent_free_space, volume_name, total_disk_space, volume_name, percent_free_space, warning_value, critical_value);
480 }
481
482 /* check to see if DS Database is open or closed */
483 } else if (vars_to_check == DSDB) {
484
485 close(sd);
486 my_tcp_connect(server_address, server_port, &sd);
487
488 send_buffer = strdup("S11\r\n");
489 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
490 if (result != STATE_OK)
491 return result;
492 if (atoi(recv_buffer) == 1)
493 result = STATE_OK;
494 else
495 result = STATE_WARNING;
496
497 close(sd);
498 my_tcp_connect(server_address, server_port, &sd);
499
500 send_buffer = strdup("S13\r\n");
501 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
502 temp_buffer = strtok(recv_buffer, "\r\n");
503
504 xasprintf(&output_message, _("Directory Services Database is %s (DS version %s)"), (result == STATE_OK) ? "open" : "closed",
505 temp_buffer);
506
507 /* check to see if logins are enabled */
508 } else if (vars_to_check == LOGINS) {
509
510 close(sd);
511 my_tcp_connect(server_address, server_port, &sd);
512
513 send_buffer = strdup("S12\r\n");
514 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
515 if (result != STATE_OK)
516 return result;
517 if (atoi(recv_buffer) == 1)
518 result = STATE_OK;
519 else
520 result = STATE_WARNING;
521
522 xasprintf(&output_message, _("Logins are %s"), (result == STATE_OK) ? _("enabled") : _("disabled"));
523
524 /* check NRM Health Status Summary*/
525 } else if (vars_to_check == NRMH) {
526
527 xasprintf(&send_buffer, "NRMH\r\n");
528 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
529 if (result != STATE_OK)
530 return result;
531
532 nrm_health_status = atoi(recv_buffer);
533
534 if (nrm_health_status == 2) {
535 result = STATE_OK;
536 xasprintf(&output_message, _("CRITICAL - NRM Status is bad!"));
537 } else {
538 if (nrm_health_status == 1) {
539 result = STATE_WARNING;
540 xasprintf(&output_message, _("Warning - NRM Status is suspect!"));
541 }
542
543 xasprintf(&output_message, _("OK - NRM Status is good!"));
544 }
545
546 /* check packet receive buffers */
547 } else if (vars_to_check == UPRB || vars_to_check == PUPRB) {
548
549 close(sd);
550 my_tcp_connect(server_address, server_port, &sd);
551
552 xasprintf(&send_buffer, "S15\r\n");
553 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
554 if (result != STATE_OK)
555 return result;
556
557 used_packet_receive_buffers = atoi(recv_buffer);
558
559 close(sd);
560 my_tcp_connect(server_address, server_port, &sd);
561
562 xasprintf(&send_buffer, "S16\r\n");
563 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
564 if (result != STATE_OK)
565 return result;
566
567 max_packet_receive_buffers = atoi(recv_buffer);
568
569 percent_used_packet_receive_buffers =
570 (unsigned long)(((double)used_packet_receive_buffers / (double)max_packet_receive_buffers) * 100.0);
571
572 if (vars_to_check == UPRB) {
573 if (check_critical_value && used_packet_receive_buffers >= critical_value)
574 result = STATE_CRITICAL;
575 else if (check_warning_value && used_packet_receive_buffers >= warning_value)
576 result = STATE_WARNING;
577 } else {
578 if (check_critical_value && percent_used_packet_receive_buffers >= critical_value)
579 result = STATE_CRITICAL;
580 else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value)
581 result = STATE_WARNING;
582 }
583
584 xasprintf(&output_message, _("%lu of %lu (%lu%%) packet receive buffers used"), used_packet_receive_buffers,
585 max_packet_receive_buffers, percent_used_packet_receive_buffers);
586
587 /* check SAP table entries */
588 } else if (vars_to_check == SAPENTRIES) {
589
590 close(sd);
591 my_tcp_connect(server_address, server_port, &sd);
592
593 if (sap_number == -1)
594 xasprintf(&send_buffer, "S9\r\n");
595 else
596 xasprintf(&send_buffer, "S9.%d\r\n", sap_number);
597 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
598 if (result != STATE_OK)
599 return result;
600
601 sap_entries = atoi(recv_buffer);
602
603 if (check_critical_value && sap_entries >= critical_value)
604 result = STATE_CRITICAL;
605 else if (check_warning_value && sap_entries >= warning_value)
606 result = STATE_WARNING;
607
608 if (sap_number == -1)
609 xasprintf(&output_message, _("%lu entries in SAP table"), sap_entries);
610 else
611 xasprintf(&output_message, _("%lu entries in SAP table for SAP type %d"), sap_entries, sap_number);
612
613 /* check KB purgeable space on volume */
614 } else if (vars_to_check == VKP) {
615
616 close(sd);
617 my_tcp_connect(server_address, server_port, &sd);
618
619 xasprintf(&send_buffer, "VKP%s\r\n", volume_name);
620 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
621 if (result != STATE_OK)
622 return result;
623
624 if (!strcmp(recv_buffer, "-1\n")) {
625 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
626 result = STATE_CRITICAL;
627 } else {
628 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
629 if (check_critical_value && purgeable_disk_space >= critical_value)
630 result = STATE_CRITICAL;
631 else if (check_warning_value && purgeable_disk_space >= warning_value)
632 result = STATE_WARNING;
633 xasprintf(&output_message, _("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
634 purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value);
635 }
636 /* check MB purgeable space on volume */
637 } else if (vars_to_check == VMP) {
638
639 xasprintf(&send_buffer, "VMP%s\r\n", volume_name);
640 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
641 if (result != STATE_OK)
642 return result;
643
644 if (!strcmp(recv_buffer, "-1\n")) {
645 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
646 result = STATE_CRITICAL;
647 } else {
648 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
649 if (check_critical_value && purgeable_disk_space >= critical_value)
650 result = STATE_CRITICAL;
651 else if (check_warning_value && purgeable_disk_space >= warning_value)
652 result = STATE_WARNING;
653 xasprintf(&output_message, _("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "),
654 purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value);
655 }
656
657 /* check % purgeable space on volume */
658 } else if (vars_to_check == VPP) {
659
660 close(sd);
661 my_tcp_connect(server_address, server_port, &sd);
662
663 xasprintf(&send_buffer, "VKP%s\r\n", volume_name);
664 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
665 if (result != STATE_OK)
666 return result;
667
668 if (!strcmp(recv_buffer, "-1\n")) {
669
670 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
671 result = STATE_CRITICAL;
672
673 } else {
674
675 purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
676
677 close(sd);
678 my_tcp_connect(server_address, server_port, &sd);
679
680 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
681 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
682 if (result != STATE_OK)
683 return result;
684 total_disk_space = strtoul(recv_buffer, NULL, 10);
685
686 percent_purgeable_space = (unsigned long)(((double)purgeable_disk_space / (double)total_disk_space) * 100.0);
687
688 if (check_critical_value && percent_purgeable_space >= critical_value)
689 result = STATE_CRITICAL;
690 else if (check_warning_value && percent_purgeable_space >= warning_value)
691 result = STATE_WARNING;
692 purgeable_disk_space /= 1024;
693 xasprintf(&output_message, _("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"), purgeable_disk_space,
694 percent_purgeable_space, volume_name, volume_name, percent_purgeable_space, warning_value, critical_value);
695 }
696
697 /* check KB not yet purgeable space on volume */
698 } else if (vars_to_check == VKNP) {
699
700 close(sd);
701 my_tcp_connect(server_address, server_port, &sd);
702
703 xasprintf(&send_buffer, "VKNP%s\r\n", volume_name);
704 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
705 if (result != STATE_OK)
706 return result;
707
708 if (!strcmp(recv_buffer, "-1\n")) {
709 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
710 result = STATE_CRITICAL;
711 } else {
712 non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
713 if (check_critical_value && non_purgeable_disk_space >= critical_value)
714 result = STATE_CRITICAL;
715 else if (check_warning_value && non_purgeable_disk_space >= warning_value)
716 result = STATE_WARNING;
717 xasprintf(&output_message, _("%s%lu KB not yet purgeable on volume %s"), (result == STATE_OK) ? "" : _("Only "),
718 non_purgeable_disk_space, volume_name);
719 }
720
721 /* check % not yet purgeable space on volume */
722 } else if (vars_to_check == VPNP) {
723
724 close(sd);
725 my_tcp_connect(server_address, server_port, &sd);
726
727 xasprintf(&send_buffer, "VKNP%s\r\n", volume_name);
728 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
729 if (result != STATE_OK)
730 return result;
731
732 if (!strcmp(recv_buffer, "-1\n")) {
733
734 xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name);
735 result = STATE_CRITICAL;
736
737 } else {
738
739 non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10);
740
741 close(sd);
742 my_tcp_connect(server_address, server_port, &sd);
743
744 xasprintf(&send_buffer, "VKS%s\r\n", volume_name);
745 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
746 if (result != STATE_OK)
747 return result;
748 total_disk_space = strtoul(recv_buffer, NULL, 10);
749
750 percent_non_purgeable_space = (unsigned long)(((double)non_purgeable_disk_space / (double)total_disk_space) * 100.0);
751
752 if (check_critical_value && percent_non_purgeable_space >= critical_value)
753 result = STATE_CRITICAL;
754 else if (check_warning_value && percent_non_purgeable_space >= warning_value)
755 result = STATE_WARNING;
756 purgeable_disk_space /= 1024;
757 xasprintf(&output_message, _("%lu MB (%lu%%) not yet purgeable on volume %s"), non_purgeable_disk_space,
758 percent_non_purgeable_space, volume_name);
759 }
760
761 /* check # of open files */
762 } else if (vars_to_check == OFILES) {
763
764 close(sd);
765 my_tcp_connect(server_address, server_port, &sd);
766
767 xasprintf(&send_buffer, "S18\r\n");
768 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
769 if (result != STATE_OK)
770 return result;
771
772 open_files = atoi(recv_buffer);
773
774 if (check_critical_value && open_files >= critical_value)
775 result = STATE_CRITICAL;
776 else if (check_warning_value && open_files >= warning_value)
777 result = STATE_WARNING;
778
779 xasprintf(&output_message, _("%lu open files|Openfiles=%lu;%lu;%lu;0,0"), open_files, open_files, warning_value, critical_value);
780
781 /* check # of abended threads (Netware > 5.x only) */
782 } else if (vars_to_check == ABENDS) {
783
784 close(sd);
785 my_tcp_connect(server_address, server_port, &sd);
786
787 xasprintf(&send_buffer, "S17\r\n");
788 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
789 if (result != STATE_OK)
790 return result;
791
792 abended_threads = atoi(recv_buffer);
793
794 if (check_critical_value && abended_threads >= critical_value)
795 result = STATE_CRITICAL;
796 else if (check_warning_value && abended_threads >= warning_value)
797 result = STATE_WARNING;
798
799 xasprintf(&output_message, _("%lu abended threads|Abends=%lu;%lu;%lu;;"), abended_threads, abended_threads, warning_value,
800 critical_value);
801
802 /* check # of current service processes (Netware 5.x only) */
803 } else if (vars_to_check == CSPROCS) {
804
805 close(sd);
806 my_tcp_connect(server_address, server_port, &sd);
807
808 xasprintf(&send_buffer, "S20\r\n");
809 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
810 if (result != STATE_OK)
811 return result;
812
813 max_service_processes = atoi(recv_buffer);
814
815 close(sd);
816 my_tcp_connect(server_address, server_port, &sd);
817
818 xasprintf(&send_buffer, "S21\r\n");
819 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
820 if (result != STATE_OK)
821 return result;
822
823 current_service_processes = atoi(recv_buffer);
824
825 if (check_critical_value && current_service_processes >= critical_value)
826 result = STATE_CRITICAL;
827 else if (check_warning_value && current_service_processes >= warning_value)
828 result = STATE_WARNING;
829
830 xasprintf(&output_message, _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"), current_service_processes,
831 max_service_processes, current_service_processes, warning_value, critical_value, max_service_processes);
832
833 /* check # Timesync Status */
834 } else if (vars_to_check == TSYNC) {
835
836 close(sd);
837 my_tcp_connect(server_address, server_port, &sd);
838
839 xasprintf(&send_buffer, "S22\r\n");
840 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
841 if (result != STATE_OK)
842 return result;
843
844 time_sync_status = atoi(recv_buffer);
845
846 if (time_sync_status == 0) {
847 result = STATE_CRITICAL;
848 xasprintf(&output_message, _("CRITICAL - Time not in sync with network!"));
849 } else {
850 xasprintf(&output_message, _("OK - Time in sync with network!"));
851 }
852
853 /* check LRU sitting time in secondss */
854 } else if (vars_to_check == LRUS) {
855
856 close(sd);
857 my_tcp_connect(server_address, server_port, &sd);
858
859 send_buffer = strdup("S4\r\n");
860 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
861 if (result != STATE_OK)
862 return result;
863 lru_time = strtoul(recv_buffer, NULL, 10);
864
865 if (check_critical_value && lru_time <= critical_value)
866 result = STATE_CRITICAL;
867 else if (check_warning_value && lru_time <= warning_value)
868 result = STATE_WARNING;
869 xasprintf(&output_message, _("LRU sitting time = %lu seconds"), lru_time);
870
871 /* check % dirty cacheobuffers as a percentage of the total*/
872 } else if (vars_to_check == DCB) {
873
874 close(sd);
875 my_tcp_connect(server_address, server_port, &sd);
876
877 send_buffer = strdup("S6\r\n");
878 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
879 if (result != STATE_OK)
880 return result;
881 dirty_cache_buffers = atoi(recv_buffer);
882
883 if (check_critical_value && dirty_cache_buffers <= critical_value)
884 result = STATE_CRITICAL;
885 else if (check_warning_value && dirty_cache_buffers <= warning_value)
886 result = STATE_WARNING;
887 xasprintf(&output_message, _("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"), dirty_cache_buffers,
888 dirty_cache_buffers, warning_value, critical_value);
889
890 /* check % total cache buffers as a percentage of the original*/
891 } else if (vars_to_check == TCB) {
892
893 close(sd);
894 my_tcp_connect(server_address, server_port, &sd);
895
896 send_buffer = strdup("S7\r\n");
897 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
898 if (result != STATE_OK)
899 return result;
900 total_cache_buffers = atoi(recv_buffer);
901
902 if (check_critical_value && total_cache_buffers <= critical_value)
903 result = STATE_CRITICAL;
904 else if (check_warning_value && total_cache_buffers <= warning_value)
905 result = STATE_WARNING;
906 xasprintf(&output_message, _("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"), total_cache_buffers,
907 total_cache_buffers, warning_value, critical_value);
908
909 } else if (vars_to_check == DSVER) {
910
911 close(sd);
912 my_tcp_connect(server_address, server_port, &sd);
913
914 xasprintf(&send_buffer, "S13\r\n");
915 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
916 if (result != STATE_OK)
917 return result;
918
919 recv_buffer[strlen(recv_buffer) - 1] = 0;
920
921 xasprintf(&output_message, _("NDS Version %s"), recv_buffer);
922
923 } else if (vars_to_check == UPTIME) {
924
925 close(sd);
926 my_tcp_connect(server_address, server_port, &sd);
927
928 xasprintf(&send_buffer, "UPTIME\r\n");
929 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
930 if (result != STATE_OK)
931 return result;
932
933 recv_buffer[sizeof(recv_buffer) - 1] = 0;
934 recv_buffer[strlen(recv_buffer) - 1] = 0;
935
936 xasprintf(&output_message, _("Up %s"), recv_buffer);
937
938 } else if (vars_to_check == NLM) {
939
940 close(sd);
941 my_tcp_connect(server_address, server_port, &sd);
942
943 xasprintf(&send_buffer, "S24:%s\r\n", nlm_name);
944 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
945 if (result != STATE_OK)
946 return result;
947
948 recv_buffer[strlen(recv_buffer) - 1] = 0;
949 if (strcmp(recv_buffer, "-1")) {
950 xasprintf(&output_message, _("Module %s version %s is loaded"), nlm_name, recv_buffer);
951 } else {
952 result = STATE_CRITICAL;
953 xasprintf(&output_message, _("Module %s is not loaded"), nlm_name);
954 }
955 } else if (vars_to_check == NRMP) {
956
957 xasprintf(&send_buffer, "NRMP:%s\r\n", nrmp_name);
958 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
959 if (result != STATE_OK)
960 return result;
961
962 if (!strcmp(recv_buffer, "-1\n")) {
963 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmp_name);
964 result = STATE_CRITICAL;
965 } else {
966 nrmp_value = strtoul(recv_buffer, NULL, 10);
967 if (check_critical_value && nrmp_value <= critical_value)
968 result = STATE_CRITICAL;
969 else if (check_warning_value && nrmp_value <= warning_value)
970 result = STATE_WARNING;
971 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmp_name, nrmp_value, nrmp_name, nrmp_value, warning_value,
972 critical_value);
973 }
974
975 } else if (vars_to_check == NRMM) {
976
977 xasprintf(&send_buffer, "NRMM:%s\r\n", nrmm_name);
978 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
979 if (result != STATE_OK)
980 return result;
981
982 if (!strcmp(recv_buffer, "-1\n")) {
983 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmm_name);
984 result = STATE_CRITICAL;
985 } else {
986 nrmm_value = strtoul(recv_buffer, NULL, 10);
987 if (check_critical_value && nrmm_value <= critical_value)
988 result = STATE_CRITICAL;
989 else if (check_warning_value && nrmm_value <= warning_value)
990 result = STATE_WARNING;
991 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmm_name, nrmm_value, nrmm_name, nrmm_value, warning_value,
992 critical_value);
993 }
994
995 } else if (vars_to_check == NRMS) {
996
997 xasprintf(&send_buffer, "NRMS:%s\r\n", nrms_name);
998 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
999 if (result != STATE_OK)
1000 return result;
1001
1002 if (!strcmp(recv_buffer, "-1\n")) {
1003 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrms_name);
1004 result = STATE_CRITICAL;
1005 } else {
1006 nrms_value = strtoul(recv_buffer, NULL, 10);
1007 if (check_critical_value && nrms_value >= critical_value)
1008 result = STATE_CRITICAL;
1009 else if (check_warning_value && nrms_value >= warning_value)
1010 result = STATE_WARNING;
1011 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrms_name, nrms_value, nrms_name, nrms_value, warning_value,
1012 critical_value);
1013 }
1014
1015 } else if (vars_to_check == NSS1) {
1016
1017 xasprintf(&send_buffer, "NSS1:%s\r\n", nss1_name);
1018 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1019 if (result != STATE_OK)
1020 return result;
1021
1022 if (!strcmp(recv_buffer, "-1\n")) {
1023 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss1_name);
1024 result = STATE_CRITICAL;
1025 } else {
1026 nss1_value = strtoul(recv_buffer, NULL, 10);
1027 if (check_critical_value && nss1_value >= critical_value)
1028 result = STATE_CRITICAL;
1029 else if (check_warning_value && nss1_value >= warning_value)
1030 result = STATE_WARNING;
1031 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss1_name, nss1_value, nss1_name, nss1_value, warning_value,
1032 critical_value);
1033 }
1034
1035 } else if (vars_to_check == NSS2) {
1036
1037 xasprintf(&send_buffer, "NSS2:%s\r\n", nss2_name);
1038 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1039 if (result != STATE_OK)
1040 return result;
1041
1042 if (!strcmp(recv_buffer, "-1\n")) {
1043 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss2_name);
1044 result = STATE_CRITICAL;
1045 } else {
1046 nss2_value = strtoul(recv_buffer, NULL, 10);
1047 if (check_critical_value && nss2_value >= critical_value)
1048 result = STATE_CRITICAL;
1049 else if (check_warning_value && nss2_value >= warning_value)
1050 result = STATE_WARNING;
1051 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss2_name, nss2_value, nss2_name, nss2_value, warning_value,
1052 critical_value);
1053 }
1054
1055 } else if (vars_to_check == NSS3) {
1056
1057 xasprintf(&send_buffer, "NSS3:%s\r\n", nss3_name);
1058 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1059 if (result != STATE_OK)
1060 return result;
1061
1062 if (!strcmp(recv_buffer, "-1\n")) {
1063 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss3_name);
1064 result = STATE_CRITICAL;
1065 } else {
1066 nss3_value = strtoul(recv_buffer, NULL, 10);
1067 if (check_critical_value && nss3_value >= critical_value)
1068 result = STATE_CRITICAL;
1069 else if (check_warning_value && nss3_value >= warning_value)
1070 result = STATE_WARNING;
1071 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss3_name, nss3_value, nss3_name, nss3_value, warning_value,
1072 critical_value);
1073 }
1074
1075 } else if (vars_to_check == NSS4) {
1076
1077 xasprintf(&send_buffer, "NSS4:%s\r\n", nss4_name);
1078 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1079 if (result != STATE_OK)
1080 return result;
1081
1082 if (!strcmp(recv_buffer, "-1\n")) {
1083 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss4_name);
1084 result = STATE_CRITICAL;
1085 } else {
1086 nss4_value = strtoul(recv_buffer, NULL, 10);
1087 if (check_critical_value && nss4_value >= critical_value)
1088 result = STATE_CRITICAL;
1089 else if (check_warning_value && nss4_value >= warning_value)
1090 result = STATE_WARNING;
1091 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss4_name, nss4_value, nss4_name, nss4_value, warning_value,
1092 critical_value);
1093 }
1094
1095 } else if (vars_to_check == NSS5) {
1096
1097 xasprintf(&send_buffer, "NSS5:%s\r\n", nss5_name);
1098 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1099 if (result != STATE_OK)
1100 return result;
1101
1102 if (!strcmp(recv_buffer, "-1\n")) {
1103 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss5_name);
1104 result = STATE_CRITICAL;
1105 } else {
1106 nss5_value = strtoul(recv_buffer, NULL, 10);
1107 if (check_critical_value && nss5_value >= critical_value)
1108 result = STATE_CRITICAL;
1109 else if (check_warning_value && nss5_value >= warning_value)
1110 result = STATE_WARNING;
1111 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss5_name, nss5_value, nss5_name, nss5_value, warning_value,
1112 critical_value);
1113 }
1114
1115 } else if (vars_to_check == NSS6) {
1116
1117 xasprintf(&send_buffer, "NSS6:%s\r\n", nss6_name);
1118 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1119 if (result != STATE_OK)
1120 return result;
1121
1122 if (!strcmp(recv_buffer, "-1\n")) {
1123 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss6_name);
1124 result = STATE_CRITICAL;
1125 } else {
1126 nss6_value = strtoul(recv_buffer, NULL, 10);
1127 if (check_critical_value && nss6_value >= critical_value)
1128 result = STATE_CRITICAL;
1129 else if (check_warning_value && nss6_value >= warning_value)
1130 result = STATE_WARNING;
1131 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss6_name, nss6_value, nss6_name, nss6_value, warning_value,
1132 critical_value);
1133 }
1134
1135 } else if (vars_to_check == NSS7) {
1136
1137 xasprintf(&send_buffer, "NSS7:%s\r\n", nss7_name);
1138 result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer));
1139 if (result != STATE_OK)
1140 return result;
1141
1142 if (!strcmp(recv_buffer, "-1\n")) {
1143 xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss7_name);
1144 result = STATE_CRITICAL;
1145 } else {
1146 nss7_value = strtoul(recv_buffer, NULL, 10);
1147 if (check_critical_value && nss7_value >= critical_value)
1148 result = STATE_CRITICAL;
1149 else if (check_warning_value && nss7_value >= warning_value)
1150 result = STATE_WARNING;
1151 xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss7_name, nss7_value, nss7_name, nss7_value, warning_value,
1152 critical_value);
1153 }
1154
1155 } else {
1156
1157 output_message = strdup(_("Nothing to check!\n"));
1158 result = STATE_UNKNOWN;
1159 }
1160
1161 close(sd);
1162
1163 /* reset timeout */
1164 alarm(0);
1165
1166 printf("%s%s\n", netware_version, output_message);
1167
1168 return result;
1169}
1170
1171/* process command-line arguments */
1172int process_arguments(int argc, char **argv) {
1173 int c;
1174
1175 int option = 0;
1176 static struct option longopts[] = {{"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'},
1177 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'},
1178 {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'},
1179 {"osversion", no_argument, 0, 'o'}, {"version", no_argument, 0, 'V'},
1180 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
1181
1182 /* no options were supplied */
1183 if (argc < 2)
1184 return ERROR;
1185
1186 /* backwards compatibility */
1187 if (!is_option(argv[1])) {
1188 server_address = argv[1];
1189 argv[1] = argv[0];
1190 argv = &argv[1];
1191 argc--;
1192 }
1193
1194 for (c = 1; c < argc; c++) {
1195 if (strcmp("-to", argv[c]) == 0)
1196 strcpy(argv[c], "-t");
1197 else if (strcmp("-wv", argv[c]) == 0)
1198 strcpy(argv[c], "-w");
1199 else if (strcmp("-cv", argv[c]) == 0)
1200 strcpy(argv[c], "-c");
1201 }
1202
1203 while (1) {
1204 c = getopt_long(argc, argv, "+hoVH:t:c:w:p:v:", longopts, &option);
1205
1206 if (c == -1 || c == EOF || c == 1)
1207 break;
1208
1209 switch (c) {
1210 case '?': /* print short usage statement if args not parsable */
1211 usage5();
1212 case 'h': /* help */
1213 print_help();
1214 exit(STATE_UNKNOWN);
1215 case 'V': /* version */
1216 print_revision(progname, NP_VERSION);
1217 exit(STATE_UNKNOWN);
1218 case 'H': /* hostname */
1219 server_address = optarg;
1220 break;
1221 case 'o': /* display nos version */
1222 check_netware_version = true;
1223 break;
1224 case 'p': /* port */
1225 if (is_intnonneg(optarg))
1226 server_port = atoi(optarg);
1227 else
1228 die(STATE_UNKNOWN, _("Server port an integer\n"));
1229 break;
1230 case 'v':
1231 if (strlen(optarg) < 3)
1232 return ERROR;
1233 if (!strcmp(optarg, "LOAD1"))
1234 vars_to_check = LOAD1;
1235 else if (!strcmp(optarg, "LOAD5"))
1236 vars_to_check = LOAD5;
1237 else if (!strcmp(optarg, "LOAD15"))
1238 vars_to_check = LOAD15;
1239 else if (!strcmp(optarg, "CONNS"))
1240 vars_to_check = CONNS;
1241 else if (!strcmp(optarg, "LTCH"))
1242 vars_to_check = LTCH;
1243 else if (!strcmp(optarg, "DCB"))
1244 vars_to_check = DCB;
1245 else if (!strcmp(optarg, "TCB"))
1246 vars_to_check = TCB;
1247 else if (!strcmp(optarg, "CBUFF"))
1248 vars_to_check = CBUFF;
1249 else if (!strcmp(optarg, "CDBUFF"))
1250 vars_to_check = CDBUFF;
1251 else if (!strcmp(optarg, "LRUM"))
1252 vars_to_check = LRUM;
1253 else if (!strcmp(optarg, "LRUS"))
1254 vars_to_check = LRUS;
1255 else if (strncmp(optarg, "VPF", 3) == 0) {
1256 vars_to_check = VPF;
1257 volume_name = strdup(optarg + 3);
1258 if (!strcmp(volume_name, ""))
1259 volume_name = strdup("SYS");
1260 } else if (strncmp(optarg, "VKF", 3) == 0) {
1261 vars_to_check = VKF;
1262 volume_name = strdup(optarg + 3);
1263 if (!strcmp(volume_name, ""))
1264 volume_name = strdup("SYS");
1265 } else if (strncmp(optarg, "VMF", 3) == 0) {
1266 vars_to_check = VMF;
1267 volume_name = strdup(optarg + 3);
1268 if (!strcmp(volume_name, ""))
1269 volume_name = strdup("SYS");
1270 } else if (!strcmp(optarg, "DSDB"))
1271 vars_to_check = DSDB;
1272 else if (!strcmp(optarg, "LOGINS"))
1273 vars_to_check = LOGINS;
1274 else if (!strcmp(optarg, "NRMH"))
1275 vars_to_check = NRMH;
1276 else if (!strcmp(optarg, "UPRB"))
1277 vars_to_check = UPRB;
1278 else if (!strcmp(optarg, "PUPRB"))
1279 vars_to_check = PUPRB;
1280 else if (!strncmp(optarg, "SAPENTRIES", 10)) {
1281 vars_to_check = SAPENTRIES;
1282 if (strlen(optarg) > 10)
1283 sap_number = atoi(optarg + 10);
1284 else
1285 sap_number = -1;
1286 } else if (!strcmp(optarg, "OFILES"))
1287 vars_to_check = OFILES;
1288 else if (strncmp(optarg, "VKP", 3) == 0) {
1289 vars_to_check = VKP;
1290 volume_name = strdup(optarg + 3);
1291 if (!strcmp(volume_name, ""))
1292 volume_name = strdup("SYS");
1293 } else if (strncmp(optarg, "VMP", 3) == 0) {
1294 vars_to_check = VMP;
1295 volume_name = strdup(optarg + 3);
1296 if (!strcmp(volume_name, ""))
1297 volume_name = strdup("SYS");
1298 } else if (strncmp(optarg, "VMU", 3) == 0) {
1299 vars_to_check = VMU;
1300 volume_name = strdup(optarg + 3);
1301 if (!strcmp(volume_name, ""))
1302 volume_name = strdup("SYS");
1303 } else if (strncmp(optarg, "VPU", 3) == 0) {
1304 vars_to_check = VPU;
1305 volume_name = strdup(optarg + 3);
1306 if (!strcmp(volume_name, ""))
1307 volume_name = strdup("SYS");
1308 } else if (strncmp(optarg, "VPP", 3) == 0) {
1309 vars_to_check = VPP;
1310 volume_name = strdup(optarg + 3);
1311 if (!strcmp(volume_name, ""))
1312 volume_name = strdup("SYS");
1313 } else if (strncmp(optarg, "VKNP", 4) == 0) {
1314 vars_to_check = VKNP;
1315 volume_name = strdup(optarg + 4);
1316 if (!strcmp(volume_name, ""))
1317 volume_name = strdup("SYS");
1318 } else if (strncmp(optarg, "VPNP", 4) == 0) {
1319 vars_to_check = VPNP;
1320 volume_name = strdup(optarg + 4);
1321 if (!strcmp(volume_name, ""))
1322 volume_name = strdup("SYS");
1323 } else if (!strcmp(optarg, "ABENDS"))
1324 vars_to_check = ABENDS;
1325 else if (!strcmp(optarg, "CSPROCS"))
1326 vars_to_check = CSPROCS;
1327 else if (!strcmp(optarg, "TSYNC"))
1328 vars_to_check = TSYNC;
1329 else if (!strcmp(optarg, "DSVER"))
1330 vars_to_check = DSVER;
1331 else if (!strcmp(optarg, "UPTIME")) {
1332 vars_to_check = UPTIME;
1333 } else if (strncmp(optarg, "NLM:", 4) == 0) {
1334 vars_to_check = NLM;
1335 nlm_name = strdup(optarg + 4);
1336 } else if (strncmp(optarg, "NRMP", 4) == 0) {
1337 vars_to_check = NRMP;
1338 nrmp_name = strdup(optarg + 4);
1339 if (!strcmp(nrmp_name, ""))
1340 nrmp_name = strdup("AVAILABLE_MEMORY");
1341 } else if (strncmp(optarg, "NRMM", 4) == 0) {
1342 vars_to_check = NRMM;
1343 nrmm_name = strdup(optarg + 4);
1344 if (!strcmp(nrmm_name, ""))
1345 nrmm_name = strdup("AVAILABLE_CACHE_MEMORY");
1346
1347 }
1348
1349 else if (strncmp(optarg, "NRMS", 4) == 0) {
1350 vars_to_check = NRMS;
1351 nrms_name = strdup(optarg + 4);
1352 if (!strcmp(nrms_name, ""))
1353 nrms_name = strdup("USED_SWAP_SPACE");
1354
1355 }
1356
1357 else if (strncmp(optarg, "NSS1", 4) == 0) {
1358 vars_to_check = NSS1;
1359 nss1_name = strdup(optarg + 4);
1360 if (!strcmp(nss1_name, ""))
1361 nss1_name = strdup("CURRENTBUFFERCACHESIZE");
1362
1363 }
1364
1365 else if (strncmp(optarg, "NSS2", 4) == 0) {
1366 vars_to_check = NSS2;
1367 nss2_name = strdup(optarg + 4);
1368 if (!strcmp(nss2_name, ""))
1369 nss2_name = strdup("CACHEHITS");
1370
1371 }
1372
1373 else if (strncmp(optarg, "NSS3", 4) == 0) {
1374 vars_to_check = NSS3;
1375 nss3_name = strdup(optarg + 4);
1376 if (!strcmp(nss3_name, ""))
1377 nss3_name = strdup("CACHEGITPERCENT");
1378
1379 }
1380
1381 else if (strncmp(optarg, "NSS4", 4) == 0) {
1382 vars_to_check = NSS4;
1383 nss4_name = strdup(optarg + 4);
1384 if (!strcmp(nss4_name, ""))
1385 nss4_name = strdup("CURRENTOPENCOUNT");
1386
1387 }
1388
1389 else if (strncmp(optarg, "NSS5", 4) == 0) {
1390 vars_to_check = NSS5;
1391 nss5_name = strdup(optarg + 4);
1392 if (!strcmp(nss5_name, ""))
1393 nss5_name = strdup("CACHEMISSES");
1394
1395 }
1396
1397 else if (strncmp(optarg, "NSS6", 4) == 0) {
1398 vars_to_check = NSS6;
1399 nss6_name = strdup(optarg + 4);
1400 if (!strcmp(nss6_name, ""))
1401 nss6_name = strdup("PENDINGWORKSCOUNT");
1402
1403 }
1404
1405 else if (strncmp(optarg, "NSS7", 4) == 0) {
1406 vars_to_check = NSS7;
1407 nss7_name = strdup(optarg + 4);
1408 if (!strcmp(nss7_name, ""))
1409 nss7_name = strdup("CACHESIZE");
1410
1411 }
1412
1413 else
1414 return ERROR;
1415 break;
1416 case 'w': /* warning threshold */
1417 warning_value = strtoul(optarg, NULL, 10);
1418 check_warning_value = true;
1419 break;
1420 case 'c': /* critical threshold */
1421 critical_value = strtoul(optarg, NULL, 10);
1422 check_critical_value = true;
1423 break;
1424 case 't': /* timeout */
1425 socket_timeout = atoi(optarg);
1426 if (socket_timeout <= 0)
1427 return ERROR;
1428 }
1429 }
1430
1431 return OK;
1432}
1433
1434void print_help(void) {
1435 char *myport;
1436 xasprintf(&myport, "%d", PORT);
1437
1438 print_revision(progname, NP_VERSION);
1439
1440 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1441 printf(COPYRIGHT, copyright, email);
1442
1443 printf("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a"));
1444 printf("%s\n", _("Novell server to gather the requested system information."));
1445
1446 printf("\n\n");
1447
1448 print_usage();
1449
1450 printf(UT_HELP_VRSN);
1451 printf(UT_EXTRA_OPTS);
1452
1453 printf(UT_HOST_PORT, 'p', myport);
1454
1455 printf(" %s\n", "-v, --variable=STRING");
1456 printf(" %s\n", _("Variable to check. Valid variables include:"));
1457 printf(" %s\n", _("LOAD1 = 1 minute average CPU load"));
1458 printf(" %s\n", _("LOAD5 = 5 minute average CPU load"));
1459 printf(" %s\n", _("LOAD15 = 15 minute average CPU load"));
1460 printf(" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)"));
1461 printf(" %s\n", _("ABENDS = number of abended threads (NW 5.x only)"));
1462 printf(" %s\n", _("UPTIME = server uptime"));
1463 printf(" %s\n", _("LTCH = percent long term cache hits"));
1464 printf(" %s\n", _("CBUFF = current number of cache buffers"));
1465 printf(" %s\n", _("CDBUFF = current number of dirty cache buffers"));
1466 printf(" %s\n", _("DCB = dirty cache buffers as a percentage of the total"));
1467 printf(" %s\n", _("TCB = dirty cache buffers as a percentage of the original"));
1468 printf(" %s\n", _("OFILES = number of open files"));
1469 printf(" %s\n", _(" VMF<vol> = MB of free space on Volume <vol>"));
1470 printf(" %s\n", _(" VMU<vol> = MB used space on Volume <vol>"));
1471 printf(" %s\n", _(" VPU<vol> = percent used space on Volume <vol>"));
1472 printf(" %s\n", _(" VMP<vol> = MB of purgeable space on Volume <vol>"));
1473 printf(" %s\n", _(" VPF<vol> = percent free space on volume <vol>"));
1474 printf(" %s\n", _(" VKF<vol> = KB of free space on volume <vol>"));
1475 printf(" %s\n", _(" VPP<vol> = percent purgeable space on volume <vol>"));
1476 printf(" %s\n", _(" VKP<vol> = KB of purgeable space on volume <vol>"));
1477 printf(" %s\n", _(" VPNP<vol> = percent not yet purgeable space on volume <vol>"));
1478 printf(" %s\n", _(" VKNP<vol> = KB of not yet purgeable space on volume <vol>"));
1479 printf(" %s\n", _(" LRUM = LRU sitting time in minutes"));
1480 printf(" %s\n", _(" LRUS = LRU sitting time in seconds"));
1481 printf(" %s\n", _(" DSDB = check to see if DS Database is open"));
1482 printf(" %s\n", _(" DSVER = NDS version"));
1483 printf(" %s\n", _(" UPRB = used packet receive buffers"));
1484 printf(" %s\n", _(" PUPRB = percent (of max) used packet receive buffers"));
1485 printf(" %s\n", _(" SAPENTRIES = number of entries in the SAP table"));
1486 printf(" %s\n", _(" SAPENTRIES<n> = number of entries in the SAP table for SAP type <n>"));
1487 printf(" %s\n", _(" TSYNC = timesync status"));
1488 printf(" %s\n", _(" LOGINS = check to see if logins are enabled"));
1489 printf(" %s\n", _(" CONNS = number of currently licensed connections"));
1490 printf(" %s\n", _(" NRMH = NRM Summary Status"));
1491 printf(" %s\n", _(" NRMP<stat> = Returns the current value for a NRM health item"));
1492 printf(" %s\n", _(" NRMM<stat> = Returns the current memory stats from NRM"));
1493 printf(" %s\n", _(" NRMS<stat> = Returns the current Swapfile stats from NRM"));
1494 printf(" %s\n", _(" NSS1<stat> = Statistics from _Admin:Manage_NSS\\GeneralStats.xml"));
1495 printf(" %s\n", _(" NSS3<stat> = Statistics from _Admin:Manage_NSS\\NameCache.xml"));
1496 printf(" %s\n", _(" NSS4<stat> = Statistics from _Admin:Manage_NSS\\FileStats.xml"));
1497 printf(" %s\n", _(" NSS5<stat> = Statistics from _Admin:Manage_NSS\\ObjectCache.xml"));
1498 printf(" %s\n", _(" NSS6<stat> = Statistics from _Admin:Manage_NSS\\Thread.xml"));
1499 printf(" %s\n", _(" NSS7<stat> = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml"));
1500 printf(" %s\n", _(" NLM:<nlm> = check if NLM is loaded and report version"));
1501 printf(" %s\n", _(" (e.g. NLM:TSANDS.NLM)"));
1502 printf("\n");
1503 printf(" %s\n", "-w, --warning=INTEGER");
1504 printf(" %s\n", _("Threshold which will result in a warning status"));
1505 printf(" %s\n", "-c, --critical=INTEGER");
1506 printf(" %s\n", _("Threshold which will result in a critical status"));
1507 printf(" %s\n", "-o, --osversion");
1508 printf(" %s\n", _("Include server version string in results"));
1509
1510 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1511
1512 printf("\n");
1513 printf("%s\n", _("Notes:"));
1514 printf(" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG"));
1515 printf(" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check."));
1516 printf(" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)"));
1517 printf(" %s\n", _("- Values for critical thresholds should be lower than warning thresholds"));
1518 printf(" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, "));
1519 printf(" %s\n", _(" TCB, LRUS and LRUM."));
1520
1521 printf(UT_SUPPORT);
1522}
1523
1524void print_usage(void) {
1525 printf("%s\n", _("Usage:"));
1526 printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
1527}
diff --git a/plugins/check_overcr.c b/plugins/check_overcr.c
deleted file mode 100644
index 599540b7..00000000
--- a/plugins/check_overcr.c
+++ /dev/null
@@ -1,417 +0,0 @@
1/*****************************************************************************
2 *
3 * Monitoring check_overcr plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_overcr plugin
11 *
12 * This plugin attempts to contact the Over-CR collector daemon running on the
13 * remote UNIX server in order to gather the requested system information.
14 *
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 *
29 *
30 *****************************************************************************/
31
32const char *progname = "check_overcr";
33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h"
37#include "netutils.h"
38#include "utils.h"
39
40enum checkvar {
41 NONE,
42 LOAD1,
43 LOAD5,
44 LOAD15,
45 DPU,
46 PROCS,
47 NETSTAT,
48 UPTIME
49};
50
51enum {
52 PORT = 2000
53};
54
55static char *server_address = NULL;
56static int server_port = PORT;
57static double warning_value = 0L;
58static double critical_value = 0L;
59static bool check_warning_value = false;
60static bool check_critical_value = false;
61static enum checkvar vars_to_check = NONE;
62
63static int netstat_port = 0;
64static char *disk_name = NULL;
65static char *process_name = NULL;
66static char send_buffer[MAX_INPUT_BUFFER];
67
68static int process_arguments(int, char **);
69void print_usage(void);
70static void print_help(void);
71
72int main(int argc, char **argv) {
73 int result = STATE_UNKNOWN;
74 char recv_buffer[MAX_INPUT_BUFFER];
75 char temp_buffer[MAX_INPUT_BUFFER];
76 char *temp_ptr = NULL;
77 bool found_disk = false;
78 unsigned long percent_used_disk_space = 100;
79 double load;
80 double load_1min;
81 double load_5min;
82 double load_15min;
83 int port_connections = 0;
84 int processes = 0;
85 double uptime_raw_hours;
86 int uptime_raw_minutes = 0;
87 int uptime_days = 0;
88 int uptime_hours = 0;
89 int uptime_minutes = 0;
90
91 setlocale(LC_ALL, "");
92 bindtextdomain(PACKAGE, LOCALEDIR);
93 textdomain(PACKAGE);
94
95 /* Parse extra opts if any */
96 argv = np_extra_opts(&argc, argv, progname);
97
98 if (process_arguments(argc, argv) == ERROR)
99 usage4(_("Could not parse arguments"));
100
101 /* initialize alarm signal handling */
102 signal(SIGALRM, socket_timeout_alarm_handler);
103
104 /* set socket timeout */
105 alarm(socket_timeout);
106
107 result = process_tcp_request2(server_address, server_port, send_buffer, recv_buffer, sizeof(recv_buffer));
108
109 switch (vars_to_check) {
110
111 case LOAD1:
112 case LOAD5:
113 case LOAD15:
114
115 if (result != STATE_OK)
116 die(result, _("Unknown error fetching load data\n"));
117
118 temp_ptr = (char *)strtok(recv_buffer, "\r\n");
119 if (temp_ptr == NULL)
120 die(STATE_CRITICAL, _("Invalid response from server - no load information\n"));
121 else
122 load_1min = strtod(temp_ptr, NULL);
123
124 temp_ptr = (char *)strtok(NULL, "\r\n");
125 if (temp_ptr == NULL)
126 die(STATE_CRITICAL, _("Invalid response from server after load 1\n"));
127 else
128 load_5min = strtod(temp_ptr, NULL);
129
130 temp_ptr = (char *)strtok(NULL, "\r\n");
131 if (temp_ptr == NULL)
132 die(STATE_CRITICAL, _("Invalid response from server after load 5\n"));
133 else
134 load_15min = strtod(temp_ptr, NULL);
135
136 switch (vars_to_check) {
137 case LOAD1:
138 strcpy(temp_buffer, "1");
139 load = load_1min;
140 break;
141 case LOAD5:
142 strcpy(temp_buffer, "5");
143 load = load_5min;
144 break;
145 default:
146 strcpy(temp_buffer, "15");
147 load = load_15min;
148 break;
149 }
150
151 if (check_critical_value && (load >= critical_value))
152 result = STATE_CRITICAL;
153 else if (check_warning_value && (load >= warning_value))
154 result = STATE_WARNING;
155
156 die(result, _("Load %s - %s-min load average = %0.2f"), state_text(result), temp_buffer, load);
157
158 break;
159
160 case DPU:
161
162 if (result != STATE_OK)
163 die(result, _("Unknown error fetching disk data\n"));
164
165 for (temp_ptr = (char *)strtok(recv_buffer, " "); temp_ptr != NULL; temp_ptr = (char *)strtok(NULL, " ")) {
166
167 if (!strcmp(temp_ptr, disk_name)) {
168 found_disk = true;
169 temp_ptr = (char *)strtok(NULL, "%");
170 if (temp_ptr == NULL)
171 die(STATE_CRITICAL, _("Invalid response from server\n"));
172 else
173 percent_used_disk_space = strtoul(temp_ptr, NULL, 10);
174 break;
175 }
176
177 temp_ptr = (char *)strtok(NULL, "\r\n");
178 }
179
180 /* error if we couldn't find the info for the disk */
181 if (!found_disk)
182 die(STATE_CRITICAL, "CRITICAL - Disk '%s' non-existent or not mounted", disk_name);
183
184 if (check_critical_value && (percent_used_disk_space >= critical_value))
185 result = STATE_CRITICAL;
186 else if (check_warning_value && (percent_used_disk_space >= warning_value))
187 result = STATE_WARNING;
188
189 die(result, "Disk %s - %lu%% used on %s", state_text(result), percent_used_disk_space, disk_name);
190
191 break;
192
193 case NETSTAT:
194
195 if (result != STATE_OK)
196 die(result, _("Unknown error fetching network status\n"));
197 else
198 port_connections = strtod(recv_buffer, NULL);
199
200 if (check_critical_value && (port_connections >= critical_value))
201 result = STATE_CRITICAL;
202 else if (check_warning_value && (port_connections >= warning_value))
203 result = STATE_WARNING;
204
205 die(result, _("Net %s - %d connection%s on port %d"), state_text(result), port_connections, (port_connections == 1) ? "" : "s",
206 netstat_port);
207
208 break;
209
210 case PROCS:
211
212 if (result != STATE_OK)
213 die(result, _("Unknown error fetching process status\n"));
214
215 temp_ptr = (char *)strtok(recv_buffer, "(");
216 if (temp_ptr == NULL)
217 die(STATE_CRITICAL, _("Invalid response from server\n"));
218
219 temp_ptr = (char *)strtok(NULL, ")");
220 if (temp_ptr == NULL)
221 die(STATE_CRITICAL, _("Invalid response from server\n"));
222 else
223 processes = strtod(temp_ptr, NULL);
224
225 if (check_critical_value && (processes >= critical_value))
226 result = STATE_CRITICAL;
227 else if (check_warning_value && (processes >= warning_value))
228 result = STATE_WARNING;
229
230 die(result, _("Process %s - %d instance%s of %s running"), state_text(result), processes, (processes == 1) ? "" : "s",
231 process_name);
232 break;
233
234 case UPTIME:
235
236 if (result != STATE_OK)
237 return result;
238
239 uptime_raw_hours = strtod(recv_buffer, NULL);
240 uptime_raw_minutes = (unsigned long)(uptime_raw_hours * 60.0);
241
242 if (check_critical_value && (uptime_raw_minutes <= critical_value))
243 result = STATE_CRITICAL;
244 else if (check_warning_value && (uptime_raw_minutes <= warning_value))
245 result = STATE_WARNING;
246
247 uptime_days = uptime_raw_minutes / 1440;
248 uptime_raw_minutes %= 1440;
249 uptime_hours = uptime_raw_minutes / 60;
250 uptime_raw_minutes %= 60;
251 uptime_minutes = uptime_raw_minutes;
252
253 die(result, _("Uptime %s - Up %d days %d hours %d minutes"), state_text(result), uptime_days, uptime_hours, uptime_minutes);
254 break;
255
256 default:
257 die(STATE_UNKNOWN, _("Nothing to check!\n"));
258 break;
259 }
260}
261
262/* process command-line arguments */
263int process_arguments(int argc, char **argv) {
264 int c;
265
266 int option = 0;
267 static struct option longopts[] = {
268 {"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, {"critical", required_argument, 0, 'c'},
269 {"warning", required_argument, 0, 'w'}, {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'},
270 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
271
272 /* no options were supplied */
273 if (argc < 2)
274 return ERROR;
275
276 /* backwards compatibility */
277 if (!is_option(argv[1])) {
278 server_address = argv[1];
279 argv[1] = argv[0];
280 argv = &argv[1];
281 argc--;
282 }
283
284 for (c = 1; c < argc; c++) {
285 if (strcmp("-to", argv[c]) == 0)
286 strcpy(argv[c], "-t");
287 else if (strcmp("-wv", argv[c]) == 0)
288 strcpy(argv[c], "-w");
289 else if (strcmp("-cv", argv[c]) == 0)
290 strcpy(argv[c], "-c");
291 }
292
293 while (1) {
294 c = getopt_long(argc, argv, "+hVH:t:c:w:p:v:", longopts, &option);
295
296 if (c == -1 || c == EOF || c == 1)
297 break;
298
299 switch (c) {
300 case '?': /* print short usage statement if args not parsable */
301 usage5();
302 case 'h': /* help */
303 print_help();
304 exit(STATE_UNKNOWN);
305 case 'V': /* version */
306 print_revision(progname, NP_VERSION);
307 exit(STATE_UNKNOWN);
308 case 'H': /* hostname */
309 server_address = optarg;
310 break;
311 case 'p': /* port */
312 if (is_intnonneg(optarg))
313 server_port = atoi(optarg);
314 else
315 die(STATE_UNKNOWN, _("Server port an integer\n"));
316 break;
317 case 'v': /* variable */
318 if (strcmp(optarg, "LOAD") == 0) {
319 strcpy(send_buffer, "LOAD\r\nQUIT\r\n");
320 if (strcmp(optarg, "LOAD1") == 0)
321 vars_to_check = LOAD1;
322 else if (strcmp(optarg, "LOAD5") == 0)
323 vars_to_check = LOAD5;
324 else if (strcmp(optarg, "LOAD15") == 0)
325 vars_to_check = LOAD15;
326 } else if (strcmp(optarg, "UPTIME") == 0) {
327 vars_to_check = UPTIME;
328 strcpy(send_buffer, "UPTIME\r\n");
329 } else if (strstr(optarg, "PROC") == optarg) {
330 vars_to_check = PROCS;
331 process_name = strscpy(process_name, optarg + 4);
332 sprintf(send_buffer, "PROCESS %s\r\n", process_name);
333 } else if (strstr(optarg, "NET") == optarg) {
334 vars_to_check = NETSTAT;
335 netstat_port = atoi(optarg + 3);
336 sprintf(send_buffer, "NETSTAT %d\r\n", netstat_port);
337 } else if (strstr(optarg, "DPU") == optarg) {
338 vars_to_check = DPU;
339 strcpy(send_buffer, "DISKSPACE\r\n");
340 disk_name = strscpy(disk_name, optarg + 3);
341 } else
342 return ERROR;
343 break;
344 case 'w': /* warning threshold */
345 warning_value = strtoul(optarg, NULL, 10);
346 check_warning_value = true;
347 break;
348 case 'c': /* critical threshold */
349 critical_value = strtoul(optarg, NULL, 10);
350 check_critical_value = true;
351 break;
352 case 't': /* timeout */
353 socket_timeout = atoi(optarg);
354 if (socket_timeout <= 0)
355 return ERROR;
356 }
357 }
358 return OK;
359}
360
361void print_help(void) {
362 char *myport;
363 xasprintf(&myport, "%d", PORT);
364
365 print_revision(progname, NP_VERSION);
366
367 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
368 printf(COPYRIGHT, copyright, email);
369
370 printf("%s\n", _("This plugin attempts to contact the Over-CR collector daemon running on the"));
371 printf("%s\n", _("remote UNIX server in order to gather the requested system information."));
372
373 printf("\n\n");
374
375 print_usage();
376
377 printf(UT_HELP_VRSN);
378 printf(UT_EXTRA_OPTS);
379
380 printf(UT_HOST_PORT, 'p', myport);
381
382 printf(" %s\n", "-w, --warning=INTEGER");
383 printf(" %s\n", _("Threshold which will result in a warning status"));
384 printf(" %s\n", "-c, --critical=INTEGER");
385 printf(" %s\n", _("Threshold which will result in a critical status"));
386 printf(" %s\n", "-v, --variable=STRING");
387 printf(" %s\n", _("Variable to check. Valid variables include:"));
388 printf(" %s\n", _("LOAD1 = 1 minute average CPU load"));
389 printf(" %s\n", _("LOAD5 = 5 minute average CPU load"));
390 printf(" %s\n", _("LOAD15 = 15 minute average CPU load"));
391 printf(" %s\n", _("DPU<filesys> = percent used disk space on filesystem <filesys>"));
392 printf(" %s\n", _("PROC<process> = number of running processes with name <process>"));
393 printf(" %s\n", _("NET<port> = number of active connections on TCP port <port>"));
394 printf(" %s\n", _("UPTIME = system uptime in seconds"));
395
396 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
397
398 printf(UT_VERBOSE);
399
400 printf("\n");
401 printf("%s\n", _("This plugin requires that Eric Molitors' Over-CR collector daemon be"));
402 printf("%s\n", _("running on the remote server."));
403 printf("%s\n", _("Over-CR can be downloaded from http://www.molitor.org/overcr"));
404 printf("%s\n", _("This plugin was tested with version 0.99.53 of the Over-CR collector"));
405
406 printf("\n");
407 printf("%s\n", _("Notes:"));
408 printf(" %s\n", _("For the available options, the critical threshold value should always be"));
409 printf(" %s\n", _("higher than the warning threshold value, EXCEPT with the uptime variable"));
410
411 printf(UT_SUPPORT);
412}
413
414void print_usage(void) {
415 printf("%s\n", _("Usage:"));
416 printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname);
417}
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 6613634d..793a686f 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -28,6 +28,7 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_pgsql"; 32const char *progname = "check_pgsql";
32const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
@@ -35,51 +36,39 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 36#include "common.h"
36#include "utils.h" 37#include "utils.h"
37#include "utils_cmd.h" 38#include "utils_cmd.h"
39#include "check_pgsql.d/config.h"
40#include "thresholds.h"
38 41
39#include "netutils.h" 42#include "netutils.h"
40#include <libpq-fe.h> 43#include <libpq-fe.h>
41#include <pg_config_manual.h> 44#include <pg_config_manual.h>
42 45
43#define DEFAULT_DB "template1"
44#define DEFAULT_HOST "127.0.0.1" 46#define DEFAULT_HOST "127.0.0.1"
45 47
46/* return the PSQL server version as a 3-tuple */ 48/* return the PSQL server version as a 3-tuple */
47#define PSQL_SERVER_VERSION3(server_version) \ 49#define PSQL_SERVER_VERSION3(server_version) \
48 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 50 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \
49 (server_version) - (int)((server_version) / 100) * 100 51 (server_version) - (int)((server_version) / 100) * 100
50/* return true if the given host is a UNIX domain socket */ 52/* return true if the given host is a UNIX domain socket */
51#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) 53#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
52/* return a 3-tuple identifying a host/port independent of the socket type */ 54/* return a 3-tuple identifying a host/port independent of the socket type */
53#define PSQL_SOCKET3(host, port) \ 55#define PSQL_SOCKET3(host, port) \
54 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port 56 ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \
57 PSQL_IS_UNIX_DOMAIN_SOCKET(host) ? "/.s.PGSQL." : ":", port
55 58
56enum { 59typedef struct {
57 DEFAULT_PORT = 5432, 60 int errorcode;
58 DEFAULT_WARN = 2, 61 check_pgsql_config config;
59 DEFAULT_CRIT = 8 62} check_pgsql_config_wrapper;
60}; 63static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
61 64
62static int process_arguments(int /*argc*/, char ** /*argv*/);
63static void print_help(void); 65static void print_help(void);
64static bool is_pg_logname(char * /*username*/); 66static bool is_pg_logname(char * /*username*/);
65static int do_query(PGconn * /*conn*/, char * /*query*/); 67static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[],
68 thresholds * /*qthresholds*/, char * /*query_warning*/,
69 char * /*query_critical*/);
66void print_usage(void); 70void print_usage(void);
67 71
68static char *pghost = NULL; /* host name of the backend server */
69static char *pgport = NULL; /* port of the backend server */
70static char *pgoptions = NULL;
71static char *pgtty = NULL;
72static char dbName[NAMEDATALEN] = DEFAULT_DB;
73static char *pguser = NULL;
74static char *pgpasswd = NULL;
75static char *pgparams = NULL;
76static double twarn = (double)DEFAULT_WARN;
77static double tcrit = (double)DEFAULT_CRIT;
78static char *pgquery = NULL;
79static char *pgqueryname = NULL;
80static char *query_warning = NULL;
81static char *query_critical = NULL;
82static thresholds *qthresholds = NULL;
83static int verbose = 0; 72static int verbose = 0;
84 73
85#define OPTID_QUERYNAME -1000 74#define OPTID_QUERYNAME -1000
@@ -139,21 +128,19 @@ int main(int argc, char **argv) {
139 bindtextdomain(PACKAGE, LOCALEDIR); 128 bindtextdomain(PACKAGE, LOCALEDIR);
140 textdomain(PACKAGE); 129 textdomain(PACKAGE);
141 130
142 /* begin, by setting the parameters for a backend connection if the
143 * parameters are null, then the system will try to use reasonable
144 * defaults by looking up environment variables or, failing that,
145 * using hardwired constants */
146
147 pgoptions = NULL; /* special options to start up the backend server */
148 pgtty = NULL; /* debugging tty for the backend server */
149
150 /* Parse extra opts if any */ 131 /* Parse extra opts if any */
151 argv = np_extra_opts(&argc, argv, progname); 132 argv = np_extra_opts(&argc, argv, progname);
152 133
153 if (process_arguments(argc, argv) == ERROR) 134 check_pgsql_config_wrapper tmp_config = process_arguments(argc, argv);
135 if (tmp_config.errorcode == ERROR) {
154 usage4(_("Could not parse arguments")); 136 usage4(_("Could not parse arguments"));
155 if (verbose > 2) 137 }
138
139 const check_pgsql_config config = tmp_config.config;
140
141 if (verbose > 2) {
156 printf("Arguments initialized\n"); 142 printf("Arguments initialized\n");
143 }
157 144
158 /* Set signal handling and alarm */ 145 /* Set signal handling and alarm */
159 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 146 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
@@ -162,25 +149,33 @@ int main(int argc, char **argv) {
162 alarm(timeout_interval); 149 alarm(timeout_interval);
163 150
164 char *conninfo = NULL; 151 char *conninfo = NULL;
165 if (pgparams) 152 if (config.pgparams) {
166 asprintf(&conninfo, "%s ", pgparams); 153 asprintf(&conninfo, "%s ", config.pgparams);
167 154 }
168 asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", dbName); 155
169 if (pghost) 156 asprintf(&conninfo, "%sdbname = '%s'", conninfo ? conninfo : "", config.dbName);
170 asprintf(&conninfo, "%s host = '%s'", conninfo, pghost); 157 if (config.pghost) {
171 if (pgport) 158 asprintf(&conninfo, "%s host = '%s'", conninfo, config.pghost);
172 asprintf(&conninfo, "%s port = '%s'", conninfo, pgport); 159 }
173 if (pgoptions) 160 if (config.pgport) {
174 asprintf(&conninfo, "%s options = '%s'", conninfo, pgoptions); 161 asprintf(&conninfo, "%s port = '%s'", conninfo, config.pgport);
162 }
163 if (config.pgoptions) {
164 asprintf(&conninfo, "%s options = '%s'", conninfo, config.pgoptions);
165 }
175 /* if (pgtty) -- ignored by PQconnectdb */ 166 /* if (pgtty) -- ignored by PQconnectdb */
176 if (pguser) 167 if (config.pguser) {
177 asprintf(&conninfo, "%s user = '%s'", conninfo, pguser); 168 asprintf(&conninfo, "%s user = '%s'", conninfo, config.pguser);
169 }
178 170
179 if (verbose) /* do not include password (see right below) in output */ 171 if (verbose) { /* do not include password (see right below) in output */
180 printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo, pgpasswd ? " password = <hidden>" : ""); 172 printf("Connecting to PostgreSQL using conninfo: %s%s\n", conninfo,
173 config.pgpasswd ? " password = <hidden>" : "");
174 }
181 175
182 if (pgpasswd) 176 if (config.pgpasswd) {
183 asprintf(&conninfo, "%s password = '%s'", conninfo, pgpasswd); 177 asprintf(&conninfo, "%s password = '%s'", conninfo, config.pgpasswd);
178 }
184 179
185 /* make a connection to the database */ 180 /* make a connection to the database */
186 struct timeval start_timeval; 181 struct timeval start_timeval;
@@ -193,25 +188,27 @@ int main(int argc, char **argv) {
193 --end_timeval.tv_sec; 188 --end_timeval.tv_sec;
194 end_timeval.tv_usec += 1000000; 189 end_timeval.tv_usec += 1000000;
195 } 190 }
196 double elapsed_time = 191 double elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec) +
197 (double)(end_timeval.tv_sec - start_timeval.tv_sec) + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0; 192 ((double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0);
198 193
199 if (verbose) 194 if (verbose) {
200 printf("Time elapsed: %f\n", elapsed_time); 195 printf("Time elapsed: %f\n", elapsed_time);
196 }
201 197
202 /* check to see that the backend connection was successfully made */ 198 /* check to see that the backend connection was successfully made */
203 if (verbose) 199 if (verbose) {
204 printf("Verifying connection\n"); 200 printf("Verifying connection\n");
201 }
205 if (PQstatus(conn) == CONNECTION_BAD) { 202 if (PQstatus(conn) == CONNECTION_BAD) {
206 printf(_("CRITICAL - no connection to '%s' (%s).\n"), dbName, PQerrorMessage(conn)); 203 printf(_("CRITICAL - no connection to '%s' (%s).\n"), config.dbName, PQerrorMessage(conn));
207 PQfinish(conn); 204 PQfinish(conn);
208 return STATE_CRITICAL; 205 return STATE_CRITICAL;
209 } 206 }
210 207
211 int status = STATE_UNKNOWN; 208 mp_state_enum status = STATE_UNKNOWN;
212 if (elapsed_time > tcrit) { 209 if (elapsed_time > config.tcrit) {
213 status = STATE_CRITICAL; 210 status = STATE_CRITICAL;
214 } else if (elapsed_time > twarn) { 211 } else if (elapsed_time > config.twarn) {
215 status = STATE_WARNING; 212 status = STATE_WARNING;
216 } else { 213 } else {
217 status = STATE_OK; 214 status = STATE_OK;
@@ -224,25 +221,29 @@ int main(int argc, char **argv) {
224 printf("Successfully connected to database %s (user %s) " 221 printf("Successfully connected to database %s (user %s) "
225 "at server %s%s%s (server version: %d.%d.%d, " 222 "at server %s%s%s (server version: %d.%d.%d, "
226 "protocol version: %d, pid: %d)\n", 223 "protocol version: %d, pid: %d)\n",
227 PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)), PSQL_SERVER_VERSION3(server_version), 224 PQdb(conn), PQuser(conn), PSQL_SOCKET3(server_host, PQport(conn)),
228 PQprotocolVersion(conn), PQbackendPID(conn)); 225 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
229 } 226 }
230 227
231 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), dbName, elapsed_time, 228 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time,
232 fperfdata("time", elapsed_time, "s", !!(twarn > 0.0), twarn, !!(tcrit > 0.0), tcrit, true, 0, false, 0)); 229 fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn,
230 (config.tcrit > 0.0), config.tcrit, true, 0, false, 0));
233 231
234 int query_status = STATE_UNKNOWN; 232 mp_state_enum query_status = STATE_UNKNOWN;
235 if (pgquery) 233 if (config.pgquery) {
236 query_status = do_query(conn, pgquery); 234 query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds,
235 config.query_warning, config.query_critical);
236 }
237 237
238 if (verbose) 238 if (verbose) {
239 printf("Closing connection\n"); 239 printf("Closing connection\n");
240 }
240 PQfinish(conn); 241 PQfinish(conn);
241 return (pgquery && query_status > status) ? query_status : status; 242 return (config.pgquery && query_status > status) ? query_status : status;
242} 243}
243 244
244/* process command-line arguments */ 245/* process command-line arguments */
245int process_arguments(int argc, char **argv) { 246check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
246 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 247 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
247 {"version", no_argument, 0, 'V'}, 248 {"version", no_argument, 0, 'V'},
248 {"timeout", required_argument, 0, 't'}, 249 {"timeout", required_argument, 0, 't'},
@@ -262,12 +263,19 @@ int process_arguments(int argc, char **argv) {
262 {"verbose", no_argument, 0, 'v'}, 263 {"verbose", no_argument, 0, 'v'},
263 {0, 0, 0, 0}}; 264 {0, 0, 0, 0}};
264 265
266 check_pgsql_config_wrapper result = {
267 .errorcode = OK,
268 .config = check_pgsql_config_init(),
269 };
270
265 while (true) { 271 while (true) {
266 int option = 0; 272 int option = 0;
267 int option_char = getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option); 273 int option_char =
274 getopt_long(argc, argv, "hVt:c:w:H:P:d:l:p:a:o:q:C:W:v", longopts, &option);
268 275
269 if (option_char == EOF) 276 if (option_char == EOF) {
270 break; 277 break;
278 }
271 279
272 switch (option_char) { 280 switch (option_char) {
273 case '?': /* usage */ 281 case '?': /* usage */
@@ -279,68 +287,75 @@ int process_arguments(int argc, char **argv) {
279 print_revision(progname, NP_VERSION); 287 print_revision(progname, NP_VERSION);
280 exit(STATE_UNKNOWN); 288 exit(STATE_UNKNOWN);
281 case 't': /* timeout period */ 289 case 't': /* timeout period */
282 if (!is_integer(optarg)) 290 if (!is_integer(optarg)) {
283 usage2(_("Timeout interval must be a positive integer"), optarg); 291 usage2(_("Timeout interval must be a positive integer"), optarg);
284 else 292 } else {
285 timeout_interval = atoi(optarg); 293 timeout_interval = atoi(optarg);
294 }
286 break; 295 break;
287 case 'c': /* critical time threshold */ 296 case 'c': /* critical time threshold */
288 if (!is_nonnegative(optarg)) 297 if (!is_nonnegative(optarg)) {
289 usage2(_("Critical threshold must be a positive integer"), optarg); 298 usage2(_("Critical threshold must be a positive integer"), optarg);
290 else 299 } else {
291 tcrit = strtod(optarg, NULL); 300 result.config.tcrit = strtod(optarg, NULL);
301 }
292 break; 302 break;
293 case 'w': /* warning time threshold */ 303 case 'w': /* warning time threshold */
294 if (!is_nonnegative(optarg)) 304 if (!is_nonnegative(optarg)) {
295 usage2(_("Warning threshold must be a positive integer"), optarg); 305 usage2(_("Warning threshold must be a positive integer"), optarg);
296 else 306 } else {
297 twarn = strtod(optarg, NULL); 307 result.config.twarn = strtod(optarg, NULL);
308 }
298 break; 309 break;
299 case 'C': /* critical query threshold */ 310 case 'C': /* critical query threshold */
300 query_critical = optarg; 311 result.config.query_critical = optarg;
301 break; 312 break;
302 case 'W': /* warning query threshold */ 313 case 'W': /* warning query threshold */
303 query_warning = optarg; 314 result.config.query_warning = optarg;
304 break; 315 break;
305 case 'H': /* host */ 316 case 'H': /* host */
306 if ((*optarg != '/') && (!is_host(optarg))) 317 if ((*optarg != '/') && (!is_host(optarg))) {
307 usage2(_("Invalid hostname/address"), optarg); 318 usage2(_("Invalid hostname/address"), optarg);
308 else 319 } else {
309 pghost = optarg; 320 result.config.pghost = optarg;
321 }
310 break; 322 break;
311 case 'P': /* port */ 323 case 'P': /* port */
312 if (!is_integer(optarg)) 324 if (!is_integer(optarg)) {
313 usage2(_("Port must be a positive integer"), optarg); 325 usage2(_("Port must be a positive integer"), optarg);
314 else 326 } else {
315 pgport = optarg; 327 result.config.pgport = optarg;
328 }
316 break; 329 break;
317 case 'd': /* database name */ 330 case 'd': /* database name */
318 if (strlen(optarg) >= NAMEDATALEN) { 331 if (strlen(optarg) >= NAMEDATALEN) {
319 usage2(_("Database name exceeds the maximum length"), optarg); 332 usage2(_("Database name exceeds the maximum length"), optarg);
320 } 333 }
321 snprintf(dbName, NAMEDATALEN, "%s", optarg); 334 snprintf(result.config.dbName, NAMEDATALEN, "%s", optarg);
322 break; 335 break;
323 case 'l': /* login name */ 336 case 'l': /* login name */
324 if (!is_pg_logname(optarg)) 337 if (!is_pg_logname(optarg)) {
325 usage2(_("User name is not valid"), optarg); 338 usage2(_("User name is not valid"), optarg);
326 else 339 } else {
327 pguser = optarg; 340 result.config.pguser = optarg;
341 }
328 break; 342 break;
329 case 'p': /* authentication password */ 343 case 'p': /* authentication password */
330 case 'a': 344 case 'a':
331 pgpasswd = optarg; 345 result.config.pgpasswd = optarg;
332 break; 346 break;
333 case 'o': 347 case 'o':
334 if (pgparams) 348 if (result.config.pgparams) {
335 asprintf(&pgparams, "%s %s", pgparams, optarg); 349 asprintf(&result.config.pgparams, "%s %s", result.config.pgparams, optarg);
336 else 350 } else {
337 asprintf(&pgparams, "%s", optarg); 351 asprintf(&result.config.pgparams, "%s", optarg);
352 }
338 break; 353 break;
339 case 'q': 354 case 'q':
340 pgquery = optarg; 355 result.config.pgquery = optarg;
341 break; 356 break;
342 case OPTID_QUERYNAME: 357 case OPTID_QUERYNAME:
343 pgqueryname = optarg; 358 result.config.pgqueryname = optarg;
344 break; 359 break;
345 case 'v': 360 case 'v':
346 verbose++; 361 verbose++;
@@ -348,9 +363,10 @@ int process_arguments(int argc, char **argv) {
348 } 363 }
349 } 364 }
350 365
351 set_thresholds(&qthresholds, query_warning, query_critical); 366 set_thresholds(&result.config.qthresholds, result.config.query_warning,
367 result.config.query_critical);
352 368
353 return OK; 369 return result;
354} 370}
355 371
356/** 372/**
@@ -378,8 +394,9 @@ should be added.</para>
378******************************************************************************/ 394******************************************************************************/
379 395
380bool is_pg_logname(char *username) { 396bool is_pg_logname(char *username) {
381 if (strlen(username) > NAMEDATALEN - 1) 397 if (strlen(username) > NAMEDATALEN - 1) {
382 return (false); 398 return (false);
399 }
383 return (true); 400 return (true);
384} 401}
385 402
@@ -394,7 +411,7 @@ bool is_pg_logname(char *username) {
394void print_help(void) { 411void print_help(void) {
395 char *myport; 412 char *myport;
396 413
397 xasprintf(&myport, "%d", DEFAULT_PORT); 414 xasprintf(&myport, "%d", 5432);
398 415
399 print_revision(progname, NP_VERSION); 416 print_revision(progname, NP_VERSION);
400 417
@@ -447,29 +464,39 @@ void print_help(void) {
447 464
448 printf(" %s\n", _("If a query is specified using the -q option, it will be executed after")); 465 printf(" %s\n", _("If a query is specified using the -q option, it will be executed after"));
449 printf(" %s\n", _("connecting to the server. The result from the query has to be numeric.")); 466 printf(" %s\n", _("connecting to the server. The result from the query has to be numeric."));
450 printf(" %s\n", _("Multiple SQL commands, separated by semicolon, are allowed but the result ")); 467 printf(" %s\n",
468 _("Multiple SQL commands, separated by semicolon, are allowed but the result "));
451 printf(" %s\n", _("of the last command is taken into account only. The value of the first")); 469 printf(" %s\n", _("of the last command is taken into account only. The value of the first"));
452 printf(" %s\n", _("column in the first row is used as the check result. If a second column is")); 470 printf(" %s\n",
471 _("column in the first row is used as the check result. If a second column is"));
453 printf(" %s\n", _("present in the result set, this is added to the plugin output with a")); 472 printf(" %s\n", _("present in the result set, this is added to the plugin output with a"));
454 printf(" %s\n", _("prefix of \"Extra Info:\". This information can be displayed in the system")); 473 printf(" %s\n",
474 _("prefix of \"Extra Info:\". This information can be displayed in the system"));
455 printf(" %s\n\n", _("executing the plugin.")); 475 printf(" %s\n\n", _("executing the plugin."));
456 476
457 printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual")); 477 printf(" %s\n", _("See the chapter \"Monitoring Database Activity\" of the PostgreSQL manual"));
458 printf(" %s\n\n", _("for details about how to access internal statistics of the database server.")); 478 printf(" %s\n\n",
479 _("for details about how to access internal statistics of the database server."));
459 480
460 printf(" %s\n", _("For a list of available connection parameters which may be used with the -o")); 481 printf(" %s\n",
461 printf(" %s\n", _("command line option, see the documentation for PQconnectdb() in the chapter")); 482 _("For a list of available connection parameters which may be used with the -o"));
483 printf(" %s\n",
484 _("command line option, see the documentation for PQconnectdb() in the chapter"));
462 printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be")); 485 printf(" %s\n", _("\"libpq - C Library\" of the PostgreSQL manual. For example, this may be"));
463 printf(" %s\n", _("used to specify a service name in pg_service.conf to be used for additional")); 486 printf(" %s\n",
487 _("used to specify a service name in pg_service.conf to be used for additional"));
464 printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:")); 488 printf(" %s\n", _("connection parameters: -o 'service=<name>' or to specify the SSL mode:"));
465 printf(" %s\n\n", _("-o 'sslmode=require'.")); 489 printf(" %s\n\n", _("-o 'sslmode=require'."));
466 490
467 printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To")); 491 printf(" %s\n", _("The plugin will connect to a local postmaster if no host is specified. To"));
468 printf(" %s\n", _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP")); 492 printf(" %s\n",
493 _("connect to a remote host, be sure that the remote postmaster accepts TCP/IP"));
469 printf(" %s\n\n", _("connections (start the postmaster with the -i option).")); 494 printf(" %s\n\n", _("connections (start the postmaster with the -i option)."));
470 495
471 printf(" %s\n", _("Typically, the monitoring user (unless the --logname option is used) should be")); 496 printf(" %s\n",
472 printf(" %s\n", _("able to connect to the database without a password. The plugin can also send")); 497 _("Typically, the monitoring user (unless the --logname option is used) should be"));
498 printf(" %s\n",
499 _("able to connect to the database without a password. The plugin can also send"));
473 printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password.")); 500 printf(" %s\n", _("a password, but no effort is made to obscure or encrypt the password."));
474 501
475 printf(UT_SUPPORT); 502 printf(UT_SUPPORT);
@@ -482,13 +509,16 @@ void print_usage(void) {
482 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); 509 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
483} 510}
484 511
485int do_query(PGconn *conn, char *query) { 512mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds,
486 if (verbose) 513 char *query_warning, char *query_critical) {
514 if (verbose) {
487 printf("Executing SQL query \"%s\".\n", query); 515 printf("Executing SQL query \"%s\".\n", query);
516 }
488 PGresult *res = PQexec(conn, query); 517 PGresult *res = PQexec(conn, query);
489 518
490 if (PGRES_TUPLES_OK != PQresultStatus(res)) { 519 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
491 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), PQerrorMessage(conn)); 520 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
521 PQerrorMessage(conn));
492 return STATE_CRITICAL; 522 return STATE_CRITICAL;
493 } 523 }
494 524
@@ -510,8 +540,9 @@ int do_query(PGconn *conn, char *query) {
510 540
511 char *endptr = NULL; 541 char *endptr = NULL;
512 double value = strtod(val_str, &endptr); 542 double value = strtod(val_str, &endptr);
513 if (verbose) 543 if (verbose) {
514 printf("Query result: %f\n", value); 544 printf("Query result: %f\n", value);
545 }
515 546
516 if (endptr == val_str) { 547 if (endptr == val_str) {
517 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); 548 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
@@ -519,11 +550,12 @@ int do_query(PGconn *conn, char *query) {
519 } 550 }
520 551
521 if ((endptr != NULL) && (*endptr != '\0')) { 552 if ((endptr != NULL) && (*endptr != '\0')) {
522 if (verbose) 553 if (verbose) {
523 printf("Garbage after value: %s.\n", endptr); 554 printf("Garbage after value: %s.\n", endptr);
555 }
524 } 556 }
525 557
526 int my_status = get_status(value, qthresholds); 558 mp_state_enum my_status = get_status(value, qthresholds);
527 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK") 559 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK")
528 : (my_status == STATE_WARNING) ? _("WARNING") 560 : (my_status == STATE_WARNING) ? _("WARNING")
529 : (my_status == STATE_CRITICAL) ? _("CRITICAL") 561 : (my_status == STATE_CRITICAL) ? _("CRITICAL")
@@ -534,7 +566,8 @@ int do_query(PGconn *conn, char *query) {
534 printf(_("'%s' returned %f"), query, value); 566 printf(_("'%s' returned %f"), query, value);
535 } 567 }
536 568
537 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", query_critical ? query_critical : ""); 569 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "",
570 query_critical ? query_critical : "");
538 if (PQnfields(res) > 1) { 571 if (PQnfields(res) > 1) {
539 char *extra_info = PQgetvalue(res, 0, 1); 572 char *extra_info = PQgetvalue(res, 0, 1);
540 if (extra_info != NULL) { 573 if (extra_info != NULL) {
diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h
new file mode 100644
index 00000000..2d4b8b89
--- /dev/null
+++ b/plugins/check_pgsql.d/config.h
@@ -0,0 +1,61 @@
1#pragma once
2
3#include "../../config.h"
4#include "thresholds.h"
5#include <stddef.h>
6#include <pg_config_manual.h>
7
8#define DEFAULT_DB "template1"
9
10enum {
11 DEFAULT_WARN = 2,
12 DEFAULT_CRIT = 8,
13};
14
15typedef struct {
16 char *pghost; /* host name of the backend server */
17 char *pgport; /* port of the backend server */
18 char *pgoptions; /* special options to start up the backend server */
19 char *pgtty; /* debugging tty for the backend server */
20 char dbName[NAMEDATALEN];
21 char *pguser;
22 char *pgpasswd;
23 char *pgparams;
24 char *pgquery;
25 char *pgqueryname;
26
27 double twarn;
28 double tcrit;
29 thresholds *qthresholds;
30 char *query_warning;
31 char *query_critical;
32} check_pgsql_config;
33
34/* begin, by setting the parameters for a backend connection if the
35 * parameters are null, then the system will try to use reasonable
36 * defaults by looking up environment variables or, failing that,
37 * using hardwired constants
38 * this targets .pgoptions and .pgtty
39 */
40
41check_pgsql_config check_pgsql_config_init() {
42 check_pgsql_config tmp = {
43 .pghost = NULL,
44 .pgport = NULL,
45 .pgoptions = NULL,
46 .pgtty = NULL,
47 .dbName = DEFAULT_DB,
48 .pguser = NULL,
49 .pgpasswd = NULL,
50 .pgparams = NULL,
51 .pgquery = NULL,
52 .pgqueryname = NULL,
53
54 .twarn = (double)DEFAULT_WARN,
55 .tcrit = (double)DEFAULT_CRIT,
56 .qthresholds = NULL,
57 .query_warning = NULL,
58 .query_critical = NULL,
59 };
60 return tmp;
61}
diff --git a/plugins/check_ping.c b/plugins/check_ping.c
index 4aafaf41..61feb958 100644
--- a/plugins/check_ping.c
+++ b/plugins/check_ping.c
@@ -36,61 +36,52 @@ const char *email = "devel@monitoring-plugins.org";
36#include "netutils.h" 36#include "netutils.h"
37#include "popen.h" 37#include "popen.h"
38#include "utils.h" 38#include "utils.h"
39#include "check_ping.d/config.h"
40#include "../lib/states.h"
39 41
40#include <signal.h> 42#include <signal.h>
41 43
42#define WARN_DUPLICATES "DUPLICATES FOUND! " 44#define WARN_DUPLICATES "DUPLICATES FOUND! "
43#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
44 45
45enum { 46typedef struct {
46 UNKNOWN_PACKET_LOSS = 200, /* 200% */ 47 int errorcode;
47 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */ 48 check_ping_config config;
48}; 49} check_ping_config_wrapper;
50static check_ping_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
51static check_ping_config_wrapper validate_arguments(check_ping_config_wrapper /*config_wrapper*/);
49 52
50static int process_arguments(int /*argc*/, char ** /*argv*/); 53static int get_threshold(char * /*arg*/, double * /*trta*/, int * /*tpl*/);
51static int get_threshold(char * /*arg*/, float * /*trta*/, int * /*tpl*/); 54
52static int validate_arguments(void); 55typedef struct {
53static int run_ping(const char *cmd, const char *addr); 56 mp_state_enum state;
54static int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr); 57 double round_trip_average;
58 int packet_loss;
59} ping_result;
60static ping_result run_ping(const char *cmd, const char *addr, double /*crta*/);
61
62static mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr);
55static void print_help(void); 63static void print_help(void);
56void print_usage(void); 64void print_usage(void);
57 65
58static bool display_html = false;
59static int wpl = UNKNOWN_PACKET_LOSS;
60static int cpl = UNKNOWN_PACKET_LOSS;
61static float wrta = UNKNOWN_TRIP_TIME;
62static float crta = UNKNOWN_TRIP_TIME;
63static char **addresses = NULL;
64static int n_addresses = 0;
65static int max_addr = 1;
66static int max_packets = -1;
67static int verbose = 0; 66static int verbose = 0;
68 67
69static float rta = UNKNOWN_TRIP_TIME;
70static int pl = UNKNOWN_PACKET_LOSS;
71
72static char *warn_text; 68static char *warn_text;
73 69
74int main(int argc, char **argv) { 70int main(int argc, char **argv) {
75 char *cmd = NULL;
76 char *rawcmd = NULL;
77 int result = STATE_UNKNOWN;
78 int this_result = STATE_UNKNOWN;
79 int i;
80
81 setlocale(LC_ALL, ""); 71 setlocale(LC_ALL, "");
82 setlocale(LC_NUMERIC, "C"); 72 setlocale(LC_NUMERIC, "C");
83 bindtextdomain(PACKAGE, LOCALEDIR); 73 bindtextdomain(PACKAGE, LOCALEDIR);
84 textdomain(PACKAGE); 74 textdomain(PACKAGE);
85 75
86 addresses = malloc(sizeof(char *) * max_addr);
87 addresses[0] = NULL;
88
89 /* Parse extra opts if any */ 76 /* Parse extra opts if any */
90 argv = np_extra_opts(&argc, argv, progname); 77 argv = np_extra_opts(&argc, argv, progname);
91 78
92 if (process_arguments(argc, argv) == ERROR) 79 check_ping_config_wrapper tmp_config = process_arguments(argc, argv);
80 if (tmp_config.errorcode == ERROR) {
93 usage4(_("Could not parse arguments")); 81 usage4(_("Could not parse arguments"));
82 }
83
84 const check_ping_config config = tmp_config.config;
94 85
95 /* Set signal handling and alarm */ 86 /* Set signal handling and alarm */
96 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { 87 if (signal(SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
@@ -105,71 +96,90 @@ int main(int argc, char **argv) {
105 alarm(timeout_interval); 96 alarm(timeout_interval);
106#endif 97#endif
107 98
108 for (i = 0; i < n_addresses; i++) { 99 int result = STATE_UNKNOWN;
109 100 char *rawcmd = NULL;
101 for (size_t i = 0; i < config.n_addresses; i++) {
110#ifdef PING6_COMMAND 102#ifdef PING6_COMMAND
111 if (address_family != AF_INET && is_inet6_addr(addresses[i])) 103 if (address_family != AF_INET && is_inet6_addr(config.addresses[i])) {
112 rawcmd = strdup(PING6_COMMAND); 104 rawcmd = strdup(PING6_COMMAND);
113 else 105 } else {
114 rawcmd = strdup(PING_COMMAND); 106 rawcmd = strdup(PING_COMMAND);
107 }
115#else 108#else
116 rawcmd = strdup(PING_COMMAND); 109 rawcmd = strdup(PING_COMMAND);
117#endif 110#endif
118 111
119 /* does the host address of number of packets argument come first? */ 112 char *cmd = NULL;
113
114 /* does the host address of number of packets argument come first? */
120#ifdef PING_PACKETS_FIRST 115#ifdef PING_PACKETS_FIRST
121# ifdef PING_HAS_TIMEOUT 116# ifdef PING_HAS_TIMEOUT
122 xasprintf(&cmd, rawcmd, timeout_interval, max_packets, addresses[i]); 117 xasprintf(&cmd, rawcmd, timeout_interval, config.max_packets, config.addresses[i]);
123# else 118# else
124 xasprintf(&cmd, rawcmd, max_packets, addresses[i]); 119 xasprintf(&cmd, rawcmd, config.max_packets, config.addresses[i]);
125# endif 120# endif
126#else 121#else
127 xasprintf(&cmd, rawcmd, addresses[i], max_packets); 122 xasprintf(&cmd, rawcmd, config.addresses[i], config.max_packets);
128#endif 123#endif
129 124
130 if (verbose >= 2) 125 if (verbose >= 2) {
131 printf("CMD: %s\n", cmd); 126 printf("CMD: %s\n", cmd);
127 }
132 128
133 /* run the command */ 129 /* run the command */
134 this_result = run_ping(cmd, addresses[i]);
135 130
136 if (pl == UNKNOWN_PACKET_LOSS || rta < 0.0) { 131 ping_result pinged = run_ping(cmd, config.addresses[i], config.crta);
132
133 if (pinged.packet_loss == UNKNOWN_PACKET_LOSS || pinged.round_trip_average < 0.0) {
137 printf("%s\n", cmd); 134 printf("%s\n", cmd);
138 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n")); 135 die(STATE_UNKNOWN, _("CRITICAL - Could not interpret output from ping command\n"));
139 } 136 }
140 137
141 if (pl >= cpl || rta >= crta || rta < 0) 138 if (pinged.packet_loss >= config.cpl || pinged.round_trip_average >= config.crta ||
142 this_result = STATE_CRITICAL; 139 pinged.round_trip_average < 0) {
143 else if (pl >= wpl || rta >= wrta) 140 pinged.state = STATE_CRITICAL;
144 this_result = STATE_WARNING; 141 } else if (pinged.packet_loss >= config.wpl || pinged.round_trip_average >= config.wrta) {
145 else if (pl >= 0 && rta >= 0) 142 pinged.state = STATE_WARNING;
146 this_result = max_state(STATE_OK, this_result); 143 } else if (pinged.packet_loss >= 0 && pinged.round_trip_average >= 0) {
147 144 pinged.state = max_state(STATE_OK, pinged.state);
148 if (n_addresses > 1 && this_result != STATE_UNKNOWN) 145 }
149 die(STATE_OK, "%s is alive\n", addresses[i]); 146
150 147 if (config.n_addresses > 1 && pinged.state != STATE_UNKNOWN) {
151 if (display_html == true) 148 die(STATE_OK, "%s is alive\n", config.addresses[i]);
152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, addresses[i]); 149 }
153 if (pl == 100) 150
154 printf(_("PING %s - %sPacket loss = %d%%"), state_text(this_result), warn_text, pl); 151 if (config.display_html) {
155 else 152 printf("<A HREF='%s/traceroute.cgi?%s'>", CGIURL, config.addresses[i]);
156 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(this_result), warn_text, pl, rta); 153 }
157 if (display_html == true) 154 if (pinged.packet_loss == 100) {
155 printf(_("PING %s - %sPacket loss = %d%%"), state_text(pinged.state), warn_text,
156 pinged.packet_loss);
157 } else {
158 printf(_("PING %s - %sPacket loss = %d%%, RTA = %2.2f ms"), state_text(pinged.state),
159 warn_text, pinged.packet_loss, pinged.round_trip_average);
160 }
161 if (config.display_html) {
158 printf("</A>"); 162 printf("</A>");
163 }
159 164
160 /* Print performance data */ 165 /* Print performance data */
161 if (pl != 100) { 166 if (pinged.packet_loss != 100) {
162 printf("|%s", 167 printf("|%s",
163 fperfdata("rta", (double)rta, "ms", wrta > 0 ? true : false, wrta, crta > 0 ? true : false, crta, true, 0, false, 0)); 168 fperfdata("rta", pinged.round_trip_average, "ms", (bool)(config.wrta > 0),
169 config.wrta, (bool)(config.crta > 0), config.crta, true, 0, false, 0));
164 } else { 170 } else {
165 printf("| rta=U;%f;%f;;", wrta, crta); 171 printf("| rta=U;%f;%f;;", config.wrta, config.crta);
166 } 172 }
167 printf(" %s\n", perfdata("pl", (long)pl, "%", wpl > 0 ? true : false, wpl, cpl > 0 ? true : false, cpl, true, 0, false, 0));
168 173
169 if (verbose >= 2) 174 printf(" %s\n",
170 printf("%f:%d%% %f:%d%%\n", wrta, wpl, crta, cpl); 175 perfdata("pl", (long)pinged.packet_loss, "%", (bool)(config.wpl > 0), config.wpl,
176 (bool)(config.cpl > 0), config.cpl, true, 0, false, 0));
177
178 if (verbose >= 2) {
179 printf("%f:%d%% %f:%d%%\n", config.wrta, config.wpl, config.crta, config.cpl);
180 }
171 181
172 result = max_state(result, this_result); 182 result = max_state(result, pinged.state);
173 free(rawcmd); 183 free(rawcmd);
174 free(cmd); 184 free(cmd);
175 } 185 }
@@ -178,11 +188,7 @@ int main(int argc, char **argv) {
178} 188}
179 189
180/* process command-line arguments */ 190/* process command-line arguments */
181int process_arguments(int argc, char **argv) { 191check_ping_config_wrapper process_arguments(int argc, char **argv) {
182 int c = 1;
183 char *ptr;
184
185 int option = 0;
186 static struct option longopts[] = {STD_LONG_OPTS, 192 static struct option longopts[] = {STD_LONG_OPTS,
187 {"packets", required_argument, 0, 'p'}, 193 {"packets", required_argument, 0, 'p'},
188 {"nohtml", no_argument, 0, 'n'}, 194 {"nohtml", no_argument, 0, 'n'},
@@ -191,23 +197,35 @@ int process_arguments(int argc, char **argv) {
191 {"use-ipv6", no_argument, 0, '6'}, 197 {"use-ipv6", no_argument, 0, '6'},
192 {0, 0, 0, 0}}; 198 {0, 0, 0, 0}};
193 199
194 if (argc < 2) 200 check_ping_config_wrapper result = {
195 return ERROR; 201 .errorcode = OK,
202 .config = check_ping_config_init(),
203 };
204
205 if (argc < 2) {
206 result.errorcode = ERROR;
207 return result;
208 }
196 209
197 for (c = 1; c < argc; c++) { 210 for (int index = 1; index < argc; index++) {
198 if (strcmp("-to", argv[c]) == 0) 211 if (strcmp("-to", argv[index]) == 0) {
199 strcpy(argv[c], "-t"); 212 strcpy(argv[index], "-t");
200 if (strcmp("-nohtml", argv[c]) == 0) 213 }
201 strcpy(argv[c], "-n"); 214 if (strcmp("-nohtml", argv[index]) == 0) {
215 strcpy(argv[index], "-n");
216 }
202 } 217 }
203 218
204 while (1) { 219 int option = 0;
205 c = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option); 220 size_t max_addr = MAX_ADDR_START;
221 while (true) {
222 int option_index = getopt_long(argc, argv, "VvhnL46t:c:w:H:p:", longopts, &option);
206 223
207 if (c == -1 || c == EOF) 224 if (option_index == -1 || option_index == EOF) {
208 break; 225 break;
226 }
209 227
210 switch (c) { 228 switch (option_index) {
211 case '?': /* usage */ 229 case '?': /* usage */
212 usage5(); 230 usage5();
213 case 'h': /* help */ 231 case 'h': /* help */
@@ -234,17 +252,19 @@ int process_arguments(int argc, char **argv) {
234 usage(_("IPv6 support not available\n")); 252 usage(_("IPv6 support not available\n"));
235#endif 253#endif
236 break; 254 break;
237 case 'H': /* hostname */ 255 case 'H': /* hostname */ {
238 ptr = optarg; 256 char *ptr = optarg;
239 while (1) { 257 while (true) {
240 n_addresses++; 258 result.config.n_addresses++;
241 if (n_addresses > max_addr) { 259 if (result.config.n_addresses > max_addr) {
242 max_addr *= 2; 260 max_addr *= 2;
243 addresses = realloc(addresses, sizeof(char *) * max_addr); 261 result.config.addresses =
244 if (addresses == NULL) 262 realloc(result.config.addresses, sizeof(char *) * max_addr);
263 if (result.config.addresses == NULL) {
245 die(STATE_UNKNOWN, _("Could not realloc() addresses\n")); 264 die(STATE_UNKNOWN, _("Could not realloc() addresses\n"));
265 }
246 } 266 }
247 addresses[n_addresses - 1] = ptr; 267 result.config.addresses[result.config.n_addresses - 1] = ptr;
248 if ((ptr = index(ptr, ','))) { 268 if ((ptr = index(ptr, ','))) {
249 strcpy(ptr, ""); 269 strcpy(ptr, "");
250 ptr += sizeof(char); 270 ptr += sizeof(char);
@@ -252,219 +272,302 @@ int process_arguments(int argc, char **argv) {
252 break; 272 break;
253 } 273 }
254 } 274 }
255 break; 275 } break;
256 case 'p': /* number of packets to send */ 276 case 'p': /* number of packets to send */
257 if (is_intnonneg(optarg)) 277 if (is_intnonneg(optarg)) {
258 max_packets = atoi(optarg); 278 result.config.max_packets = atoi(optarg);
259 else 279 } else {
260 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg); 280 usage2(_("<max_packets> (%s) must be a non-negative number\n"), optarg);
281 }
261 break; 282 break;
262 case 'n': /* no HTML */ 283 case 'n': /* no HTML */
263 display_html = false; 284 result.config.display_html = false;
264 break; 285 break;
265 case 'L': /* show HTML */ 286 case 'L': /* show HTML */
266 display_html = true; 287 result.config.display_html = true;
267 break; 288 break;
268 case 'c': 289 case 'c':
269 get_threshold(optarg, &crta, &cpl); 290 get_threshold(optarg, &result.config.crta, &result.config.cpl);
270 break; 291 break;
271 case 'w': 292 case 'w':
272 get_threshold(optarg, &wrta, &wpl); 293 get_threshold(optarg, &result.config.wrta, &result.config.wpl);
273 break; 294 break;
274 } 295 }
275 } 296 }
276 297
277 c = optind; 298 int arg_counter = optind;
278 if (c == argc) 299 if (arg_counter == argc) {
279 return validate_arguments(); 300 return validate_arguments(result);
301 }
280 302
281 if (addresses[0] == NULL) { 303 if (result.config.addresses[0] == NULL) {
282 if (!is_host(argv[c])) { 304 if (!is_host(argv[arg_counter])) {
283 usage2(_("Invalid hostname/address"), argv[c]); 305 usage2(_("Invalid hostname/address"), argv[arg_counter]);
284 } else { 306 } else {
285 addresses[0] = argv[c++]; 307 result.config.addresses[0] = argv[arg_counter++];
286 n_addresses++; 308 result.config.n_addresses++;
287 if (c == argc) 309 if (arg_counter == argc) {
288 return validate_arguments(); 310 return validate_arguments(result);
311 }
289 } 312 }
290 } 313 }
291 314
292 if (wpl == UNKNOWN_PACKET_LOSS) { 315 if (result.config.wpl == UNKNOWN_PACKET_LOSS) {
293 if (!is_intpercent(argv[c])) { 316 if (!is_intpercent(argv[arg_counter])) {
294 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[c]); 317 printf(_("<wpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
295 return ERROR; 318 result.errorcode = ERROR;
296 } else { 319 return result;
297 wpl = atoi(argv[c++]); 320 }
298 if (c == argc) 321 result.config.wpl = atoi(argv[arg_counter++]);
299 return validate_arguments(); 322 if (arg_counter == argc) {
323 return validate_arguments(result);
300 } 324 }
301 } 325 }
302 326
303 if (cpl == UNKNOWN_PACKET_LOSS) { 327 if (result.config.cpl == UNKNOWN_PACKET_LOSS) {
304 if (!is_intpercent(argv[c])) { 328 if (!is_intpercent(argv[arg_counter])) {
305 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[c]); 329 printf(_("<cpl> (%s) must be an integer percentage\n"), argv[arg_counter]);
306 return ERROR; 330 result.errorcode = ERROR;
307 } else { 331 return result;
308 cpl = atoi(argv[c++]); 332 }
309 if (c == argc) 333 result.config.cpl = atoi(argv[arg_counter++]);
310 return validate_arguments(); 334 if (arg_counter == argc) {
335 return validate_arguments(result);
311 } 336 }
312 } 337 }
313 338
314 if (wrta < 0.0) { 339 if (result.config.wrta < 0.0) {
315 if (is_negative(argv[c])) { 340 if (is_negative(argv[arg_counter])) {
316 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[c]); 341 printf(_("<wrta> (%s) must be a non-negative number\n"), argv[arg_counter]);
317 return ERROR; 342 result.errorcode = ERROR;
318 } else { 343 return result;
319 wrta = atof(argv[c++]); 344 }
320 if (c == argc) 345 result.config.wrta = atof(argv[arg_counter++]);
321 return validate_arguments(); 346 if (arg_counter == argc) {
347 return validate_arguments(result);
322 } 348 }
323 } 349 }
324 350
325 if (crta < 0.0) { 351 if (result.config.crta < 0.0) {
326 if (is_negative(argv[c])) { 352 if (is_negative(argv[arg_counter])) {
327 printf(_("<crta> (%s) must be a non-negative number\n"), argv[c]); 353 printf(_("<crta> (%s) must be a non-negative number\n"), argv[arg_counter]);
328 return ERROR; 354 result.errorcode = ERROR;
329 } else { 355 return result;
330 crta = atof(argv[c++]); 356 }
331 if (c == argc) 357 result.config.crta = atof(argv[arg_counter++]);
332 return validate_arguments(); 358 if (arg_counter == argc) {
359 return validate_arguments(result);
333 } 360 }
334 } 361 }
335 362
336 if (max_packets == -1) { 363 if (result.config.max_packets == -1) {
337 if (is_intnonneg(argv[c])) { 364 if (is_intnonneg(argv[arg_counter])) {
338 max_packets = atoi(argv[c++]); 365 result.config.max_packets = atoi(argv[arg_counter++]);
339 } else { 366 } else {
340 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[c]); 367 printf(_("<max_packets> (%s) must be a non-negative number\n"), argv[arg_counter]);
341 return ERROR; 368 result.errorcode = ERROR;
369 return result;
342 } 370 }
343 } 371 }
344 372
345 return validate_arguments(); 373 return validate_arguments(result);
346} 374}
347 375
348int get_threshold(char *arg, float *trta, int *tpl) { 376int get_threshold(char *arg, double *trta, int *tpl) {
349 if (is_intnonneg(arg) && sscanf(arg, "%f", trta) == 1) 377 if (is_intnonneg(arg) && sscanf(arg, "%lf", trta) == 1) {
350 return OK; 378 return OK;
351 else if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%f%*[:,]%d%%", trta, tpl) == 2) 379 }
380
381 if (strpbrk(arg, ",:") && strstr(arg, "%") && sscanf(arg, "%lf%*[:,]%d%%", trta, tpl) == 2) {
352 return OK; 382 return OK;
353 else if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) 383 }
384
385 if (strstr(arg, "%") && sscanf(arg, "%d%%", tpl) == 1) {
354 return OK; 386 return OK;
387 }
355 388
356 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg); 389 usage2(_("%s: Warning threshold must be integer or percentage!\n\n"), arg);
357 return STATE_UNKNOWN; 390 return STATE_UNKNOWN;
358} 391}
359 392
360int validate_arguments() { 393check_ping_config_wrapper validate_arguments(check_ping_config_wrapper config_wrapper) {
361 float max_seconds; 394 if (config_wrapper.config.wrta < 0.0) {
362 int i;
363
364 if (wrta < 0.0) {
365 printf(_("<wrta> was not set\n")); 395 printf(_("<wrta> was not set\n"));
366 return ERROR; 396 config_wrapper.errorcode = ERROR;
367 } else if (crta < 0.0) { 397 return config_wrapper;
398 }
399
400 if (config_wrapper.config.crta < 0.0) {
368 printf(_("<crta> was not set\n")); 401 printf(_("<crta> was not set\n"));
369 return ERROR; 402 config_wrapper.errorcode = ERROR;
370 } else if (wpl == UNKNOWN_PACKET_LOSS) { 403 return config_wrapper;
404 }
405
406 if (config_wrapper.config.wpl == UNKNOWN_PACKET_LOSS) {
371 printf(_("<wpl> was not set\n")); 407 printf(_("<wpl> was not set\n"));
372 return ERROR; 408 config_wrapper.errorcode = ERROR;
373 } else if (cpl == UNKNOWN_PACKET_LOSS) { 409 return config_wrapper;
410 }
411
412 if (config_wrapper.config.cpl == UNKNOWN_PACKET_LOSS) {
374 printf(_("<cpl> was not set\n")); 413 printf(_("<cpl> was not set\n"));
375 return ERROR; 414 config_wrapper.errorcode = ERROR;
376 } else if (wrta > crta) { 415 return config_wrapper;
377 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), wrta, crta); 416 }
378 return ERROR; 417
379 } else if (wpl > cpl) { 418 if (config_wrapper.config.wrta > config_wrapper.config.crta) {
380 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), wpl, cpl); 419 printf(_("<wrta> (%f) cannot be larger than <crta> (%f)\n"), config_wrapper.config.wrta,
381 return ERROR; 420 config_wrapper.config.crta);
421 config_wrapper.errorcode = ERROR;
422 return config_wrapper;
382 } 423 }
383 424
384 if (max_packets == -1) 425 if (config_wrapper.config.wpl > config_wrapper.config.cpl) {
385 max_packets = DEFAULT_MAX_PACKETS; 426 printf(_("<wpl> (%d) cannot be larger than <cpl> (%d)\n"), config_wrapper.config.wpl,
427 config_wrapper.config.cpl);
428 config_wrapper.errorcode = ERROR;
429 return config_wrapper;
430 }
386 431
387 max_seconds = crta / 1000.0 * max_packets + max_packets; 432 if (config_wrapper.config.max_packets == -1) {
388 if (max_seconds > timeout_interval) 433 config_wrapper.config.max_packets = DEFAULT_MAX_PACKETS;
389 timeout_interval = (int)max_seconds; 434 }
390 435
391 for (i = 0; i < n_addresses; i++) { 436 double max_seconds = (config_wrapper.config.crta / 1000.0 * config_wrapper.config.max_packets) +
392 if (!is_host(addresses[i])) 437 config_wrapper.config.max_packets;
393 usage2(_("Invalid hostname/address"), addresses[i]); 438 if (max_seconds > timeout_interval) {
439 timeout_interval = (unsigned int)max_seconds;
440 }
441
442 for (size_t i = 0; i < config_wrapper.config.n_addresses; i++) {
443 if (!is_host(config_wrapper.config.addresses[i])) {
444 usage2(_("Invalid hostname/address"), config_wrapper.config.addresses[i]);
445 }
394 } 446 }
395 447
396 if (n_addresses == 0) { 448 if (config_wrapper.config.n_addresses == 0) {
397 usage(_("You must specify a server address or host name")); 449 usage(_("You must specify a server address or host name"));
398 } 450 }
399 451
400 return OK; 452 return config_wrapper;
401} 453}
402 454
403int run_ping(const char *cmd, const char *addr) { 455ping_result run_ping(const char *cmd, const char *addr, double crta) {
404 char buf[MAX_INPUT_BUFFER]; 456 if ((child_process = spopen(cmd)) == NULL) {
405 int result = STATE_UNKNOWN;
406 int match;
407
408 if ((child_process = spopen(cmd)) == NULL)
409 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 457 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
458 }
410 459
411 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); 460 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
412 if (child_stderr == NULL) 461 if (child_stderr == NULL) {
413 printf(_("Cannot open stderr for %s\n"), cmd); 462 printf(_("Cannot open stderr for %s\n"), cmd);
463 }
414 464
415 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) { 465 char buf[MAX_INPUT_BUFFER];
466 ping_result result = {
467 .state = STATE_UNKNOWN,
468 .packet_loss = UNKNOWN_PACKET_LOSS,
469 .round_trip_average = UNKNOWN_TRIP_TIME,
470 };
416 471
417 if (verbose >= 3) 472 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_process)) {
473 if (verbose >= 3) {
418 printf("Output: %s", buf); 474 printf("Output: %s", buf);
475 }
419 476
420 result = max_state(result, error_scan(buf, addr)); 477 result.state = max_state(result.state, error_scan(buf, addr));
421 478
422 /* get the percent loss statistics */ 479 /* get the percent loss statistics */
423 match = 0; 480 int match = 0;
424 if ((sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 481 if ((sscanf(
425 (sscanf(buf, "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || 482 buf,
426 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n", &pl, &match) && match) || 483 "%*d packets transmitted, %*d packets received, +%*d errors, %d%% packet loss%n",
427 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n", &pl, &match) && match) || 484 &result.packet_loss, &match) == 1 &&
428 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n", &pl, &match) && match) || 485 match) ||
429 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n", &pl, &match) && match) || 486 (sscanf(buf,
430 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n", &pl, &match) && match) || 487 "%*d packets transmitted, %*d packets received, +%*d duplicates, %d%% packet "
431 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 488 "loss%n",
432 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n", &pl, &match) && match) || 489 &result.packet_loss, &match) == 1 &&
433 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &pl, &match) && match)) 490 match) ||
491 (sscanf(buf,
492 "%*d packets transmitted, %*d received, +%*d duplicates, %d%% packet loss%n",
493 &result.packet_loss, &match) == 1 &&
494 match) ||
495 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% packet loss%n",
496 &result.packet_loss, &match) == 1 &&
497 match) ||
498 (sscanf(buf, "%*d packets transmitted, %*d packets received, %d%% loss, time%n",
499 &result.packet_loss, &match) == 1 &&
500 match) ||
501 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% loss, time%n",
502 &result.packet_loss, &match) == 1 &&
503 match) ||
504 (sscanf(buf, "%*d packets transmitted, %*d received, %d%% packet loss, time%n",
505 &result.packet_loss, &match) == 1 &&
506 match) == 1 ||
507 (sscanf(buf, "%*d packets transmitted, %*d received, +%*d errors, %d%% packet loss%n",
508 &result.packet_loss, &match) == 1 &&
509 match) ||
510 (sscanf(buf, "%*d packets transmitted %*d received, +%*d errors, %d%% packet loss%n",
511 &result.packet_loss, &match) == 1 &&
512 match) ||
513 (sscanf(buf, "%*[^(](%d%% %*[^)])%n", &result.packet_loss, &match) == 1 && match)) {
434 continue; 514 continue;
515 }
435 516
436 /* get the round trip average */ 517 /* get the round trip average */
437 else if ((sscanf(buf, "round-trip min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || 518 if ((sscanf(buf, "round-trip min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
438 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 519 &match) == 1 &&
439 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 520 match) ||
440 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 521 (sscanf(buf, "round-trip min/avg/max/mdev = %*f/%lf/%*f/%*f%n",
441 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 522 &result.round_trip_average, &match) == 1 &&
442 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%f/%*f%n", &rta, &match) && match) || 523 match) ||
443 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%f/%*f/%*f%n", &rta, &match) && match) || 524 (sscanf(buf, "round-trip min/avg/max/sdev = %*f/%lf/%*f/%*f%n",
444 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%f/%*f/%*f ms%n", &rta, &match) && match) || 525 &result.round_trip_average, &match) == 1 &&
445 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %fms%n", &rta, &match) && match)) 526 match) ||
527 (sscanf(buf, "round-trip min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
528 &result.round_trip_average, &match) == 1 &&
529 match) ||
530 (sscanf(buf, "round-trip min/avg/max/std-dev = %*f/%lf/%*f/%*f%n",
531 &result.round_trip_average, &match) == 1 &&
532 match) ||
533 (sscanf(buf, "round-trip (ms) min/avg/max = %*f/%lf/%*f%n", &result.round_trip_average,
534 &match) == 1 &&
535 match) ||
536 (sscanf(buf, "round-trip (ms) min/avg/max/stddev = %*f/%lf/%*f/%*f%n",
537 &result.round_trip_average, &match) == 1 &&
538 match) ||
539 (sscanf(buf, "rtt min/avg/max/mdev = %*f/%lf/%*f/%*f ms%n", &result.round_trip_average,
540 &match) == 1 &&
541 match) ||
542 (sscanf(buf, "%*[^=] = %*fms, %*[^=] = %*fms, %*[^=] = %lfms%n",
543 &result.round_trip_average, &match) == 1 &&
544 match)) {
446 continue; 545 continue;
546 }
447 } 547 }
448 548
449 /* this is needed because there is no rta if all packets are lost */ 549 /* this is needed because there is no rta if all packets are lost */
450 if (pl == 100) 550 if (result.packet_loss == 100) {
451 rta = crta; 551 result.round_trip_average = crta;
552 }
452 553
453 /* check stderr, setting at least WARNING if there is output here */ 554 /* check stderr, setting at least WARNING if there is output here */
454 /* Add warning into warn_text */ 555 /* Add warning into warn_text */
455 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) { 556 while (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
456 if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") && !strstr(buf, "Warning: time of day goes back") 557 if (!strstr(buf, "WARNING - no SO_TIMESTAMP support, falling back to SIOCGSTAMP") &&
558 !strstr(buf, "Warning: time of day goes back")
457 559
458 ) { 560 ) {
459 if (verbose >= 3) { 561 if (verbose >= 3) {
460 printf("Got stderr: %s", buf); 562 printf("Got stderr: %s", buf);
461 } 563 }
462 if ((result = error_scan(buf, addr)) == STATE_OK) { 564 if ((result.state = error_scan(buf, addr)) == STATE_OK) {
463 result = STATE_WARNING; 565 result.state = STATE_WARNING;
464 if (warn_text == NULL) { 566 if (warn_text == NULL) {
465 warn_text = strdup(_("System call sent warnings to stderr ")); 567 warn_text = strdup(_("System call sent warnings to stderr "));
466 } else { 568 } else {
467 xasprintf(&warn_text, "%s %s", warn_text, _("System call sent warnings to stderr ")); 569 xasprintf(&warn_text, "%s %s", warn_text,
570 _("System call sent warnings to stderr "));
468 } 571 }
469 } 572 }
470 } 573 }
@@ -474,43 +577,48 @@ int run_ping(const char *cmd, const char *addr) {
474 577
475 spclose(child_process); 578 spclose(child_process);
476 579
477 if (warn_text == NULL) 580 if (warn_text == NULL) {
478 warn_text = strdup(""); 581 warn_text = strdup("");
582 }
479 583
480 return result; 584 return result;
481} 585}
482 586
483int error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) { 587mp_state_enum error_scan(char buf[MAX_INPUT_BUFFER], const char *addr) {
484 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") || strstr(buf, "No route")) 588 if (strstr(buf, "Network is unreachable") || strstr(buf, "Destination Net Unreachable") ||
589 strstr(buf, "No route")) {
485 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr); 590 die(STATE_CRITICAL, _("CRITICAL - Network Unreachable (%s)\n"), addr);
486 else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) 591 } else if (strstr(buf, "Destination Host Unreachable") || strstr(buf, "Address unreachable")) {
487 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr); 592 die(STATE_CRITICAL, _("CRITICAL - Host Unreachable (%s)\n"), addr);
488 else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) 593 } else if (strstr(buf, "Destination Port Unreachable") || strstr(buf, "Port unreachable")) {
489 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr); 594 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Port Unreachable (%s)\n"), addr);
490 else if (strstr(buf, "Destination Protocol Unreachable")) 595 } else if (strstr(buf, "Destination Protocol Unreachable")) {
491 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr); 596 die(STATE_CRITICAL, _("CRITICAL - Bogus ICMP: Protocol Unreachable (%s)\n"), addr);
492 else if (strstr(buf, "Destination Net Prohibited")) 597 } else if (strstr(buf, "Destination Net Prohibited")) {
493 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr); 598 die(STATE_CRITICAL, _("CRITICAL - Network Prohibited (%s)\n"), addr);
494 else if (strstr(buf, "Destination Host Prohibited")) 599 } else if (strstr(buf, "Destination Host Prohibited")) {
495 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr); 600 die(STATE_CRITICAL, _("CRITICAL - Host Prohibited (%s)\n"), addr);
496 else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) 601 } else if (strstr(buf, "Packet filtered") || strstr(buf, "Administratively prohibited")) {
497 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr); 602 die(STATE_CRITICAL, _("CRITICAL - Packet Filtered (%s)\n"), addr);
498 else if (strstr(buf, "unknown host")) 603 } else if (strstr(buf, "unknown host")) {
499 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr); 604 die(STATE_CRITICAL, _("CRITICAL - Host not found (%s)\n"), addr);
500 else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) 605 } else if (strstr(buf, "Time to live exceeded") || strstr(buf, "Time exceeded")) {
501 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr); 606 die(STATE_CRITICAL, _("CRITICAL - Time to live exceeded (%s)\n"), addr);
502 else if (strstr(buf, "Destination unreachable: ")) 607 } else if (strstr(buf, "Destination unreachable: ")) {
503 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr); 608 die(STATE_CRITICAL, _("CRITICAL - Destination Unreachable (%s)\n"), addr);
609 }
504 610
505 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) { 611 if (strstr(buf, "(DUP!)") || strstr(buf, "DUPLICATES FOUND")) {
506 if (warn_text == NULL) 612 if (warn_text == NULL) {
507 warn_text = strdup(_(WARN_DUPLICATES)); 613 warn_text = strdup(_(WARN_DUPLICATES));
508 else if (!strstr(warn_text, _(WARN_DUPLICATES)) && xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) 614 } else if (!strstr(warn_text, _(WARN_DUPLICATES)) &&
615 xasprintf(&warn_text, "%s %s", warn_text, _(WARN_DUPLICATES)) == -1) {
509 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n")); 616 die(STATE_UNKNOWN, _("Unable to realloc warn_text\n"));
510 return (STATE_WARNING); 617 }
618 return STATE_WARNING;
511 } 619 }
512 620
513 return (STATE_OK); 621 return STATE_OK;
514} 622}
515 623
516void print_help(void) { 624void print_help(void) {
@@ -550,10 +658,10 @@ void print_help(void) {
550 printf("%s\n", _("percentage of packet loss to trigger an alarm state.")); 658 printf("%s\n", _("percentage of packet loss to trigger an alarm state."));
551 659
552 printf("\n"); 660 printf("\n");
553 printf("%s\n", _("This plugin uses the ping command to probe the specified host for packet loss")); 661 printf("%s\n",
554 printf("%s\n", _("(percentage) and round trip average (milliseconds). It can produce HTML output")); 662 _("This plugin uses the ping command to probe the specified host for packet loss"));
555 printf("%s\n", _("linking to a traceroute CGI contributed by Ian Cass. The CGI can be found in")); 663 printf("%s\n",
556 printf("%s\n", _("the contrib area of the downloads section at http://www.nagios.org/")); 664 _("(percentage) and round trip average (milliseconds). It can produce HTML output."));
557 665
558 printf(UT_SUPPORT); 666 printf(UT_SUPPORT);
559} 667}
diff --git a/plugins/check_ping.d/config.h b/plugins/check_ping.d/config.h
new file mode 100644
index 00000000..eb2735a7
--- /dev/null
+++ b/plugins/check_ping.d/config.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <stdlib.h>
6
7enum {
8 UNKNOWN_PACKET_LOSS = 200, /* 200% */
9 DEFAULT_MAX_PACKETS = 5 /* default no. of ICMP ECHO packets */
10};
11
12#define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
13
14#define MAX_ADDR_START 1
15
16typedef struct {
17 bool display_html;
18 int max_packets;
19
20 char **addresses;
21 size_t n_addresses;
22
23 int wpl;
24 int cpl;
25 double wrta;
26 double crta;
27} check_ping_config;
28
29check_ping_config check_ping_config_init() {
30 check_ping_config tmp = {
31 .display_html = false,
32 .max_packets = -1,
33
34 .addresses = NULL,
35 .n_addresses = 0,
36
37 .wpl = UNKNOWN_PACKET_LOSS,
38 .cpl = UNKNOWN_PACKET_LOSS,
39 .wrta = UNKNOWN_TRIP_TIME,
40 .crta = UNKNOWN_TRIP_TIME,
41 };
42
43 tmp.addresses = calloc(MAX_ADDR_START, sizeof(char *));
44 tmp.addresses[0] = NULL;
45 return tmp;
46}
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index 1d78ccee..ae6e9c23 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -1,41 +1,41 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_procs plugin 3 * Monitoring check_procs plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_procs plugin 10 * This file contains the check_procs plugin
11* 11 *
12* Checks all processes and generates WARNING or CRITICAL states if the 12 * Checks all processes and generates WARNING or CRITICAL states if the
13* specified metric is outside the required threshold ranges. The metric 13 * specified metric is outside the required threshold ranges. The metric
14* defaults to number of processes. Search filters can be applied to limit 14 * defaults to number of processes. Search filters can be applied to limit
15* the processes to check. 15 * the processes to check.
16* 16 *
17* The parent process, check_procs itself and any child process of 17 * The parent process, check_procs itself and any child process of
18* check_procs (ps) are excluded from any checks to prevent false positives. 18 * check_procs (ps) are excluded from any checks to prevent false positives.
19* 19 *
20* 20 *
21* This program is free software: you can redistribute it and/or modify 21 * This program is free software: you can redistribute it and/or modify
22* it under the terms of the GNU General Public License as published by 22 * it under the terms of the GNU General Public License as published by
23* the Free Software Foundation, either version 3 of the License, or 23 * the Free Software Foundation, either version 3 of the License, or
24* (at your option) any later version. 24 * (at your option) any later version.
25* 25 *
26* This program is distributed in the hope that it will be useful, 26 * This program is distributed in the hope that it will be useful,
27* but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29* GNU General Public License for more details. 29 * GNU General Public License for more details.
30* 30 *
31* You should have received a copy of the GNU General Public License 31 * You should have received a copy of the GNU General Public License
32* along with this program. If not, see <http://www.gnu.org/licenses/>. 32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33* 33 *
34* 34 *
35*****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_procs"; 37const char *progname = "check_procs";
38const char *program_name = "check_procs"; /* Required for coreutils libs */ 38const char *program_name = "check_procs"; /* Required for coreutils libs */
39const char *copyright = "2000-2024"; 39const char *copyright = "2000-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
@@ -43,313 +43,297 @@ const char *email = "devel@monitoring-plugins.org";
43#include "utils.h" 43#include "utils.h"
44#include "utils_cmd.h" 44#include "utils_cmd.h"
45#include "regex.h" 45#include "regex.h"
46#include "states.h"
47#include "check_procs.d/config.h"
46 48
47#include <pwd.h> 49#include <pwd.h>
48#include <errno.h> 50#include <errno.h>
49 51
50#ifdef HAVE_SYS_STAT_H 52#ifdef HAVE_SYS_STAT_H
51#include <sys/stat.h> 53# include <sys/stat.h>
52#endif 54#endif
53 55
54static int process_arguments (int /*argc*/, char ** /*argv*/); 56typedef struct {
55static int validate_arguments (void); 57 int errorcode;
56static int convert_to_seconds (char * /*etime*/); 58 check_procs_config config;
57static void print_help (void); 59} check_procs_config_wrapper;
58void print_usage (void); 60static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
59 61static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/);
60static char *warning_range = NULL; 62
61static char *critical_range = NULL; 63static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/);
62static thresholds *procs_thresholds = NULL; 64static void print_help(void);
63 65void print_usage(void);
64static int options = 0; /* bitmask of filter criteria to test against */ 66
65#define ALL 1 67#define ALL 1
66#define STAT 2 68#define STAT 2
67#define PPID 4 69#define PPID 4
68#define USER 8 70#define USER 8
69#define PROG 16 71#define PROG 16
70#define ARGS 32 72#define ARGS 32
71#define VSZ 64 73#define VSZ 64
72#define RSS 128 74#define RSS 128
73#define PCPU 256 75#define PCPU 256
74#define ELAPSED 512 76#define ELAPSED 512
75#define EREG_ARGS 1024 77#define EREG_ARGS 1024
76#define EXCLUDE_PROGS 2048 78#define EXCLUDE_PROGS 2048
77 79
78#define KTHREAD_PARENT "kthreadd" /* the parent process of kernel threads: 80#define KTHREAD_PARENT \
79 ppid of procs are compared to pid of this proc*/ 81 "kthreadd" /* the parent process of kernel threads: \
80 82 ppid of procs are compared to pid of this proc*/
81/* Different metrics */
82char *metric_name;
83enum metric {
84 METRIC_PROCS,
85 METRIC_VSZ,
86 METRIC_RSS,
87 METRIC_CPU,
88 METRIC_ELAPSED
89};
90enum metric metric = METRIC_PROCS;
91 83
92static int verbose = 0; 84static int verbose = 0;
93static int uid; 85
94static pid_t ppid; 86static int stat_exe(const pid_t pid, struct stat *buf) {
95static int vsz;
96static int rss;
97static float pcpu;
98static char *statopts;
99static char *prog;
100static char *exclude_progs;
101static char **exclude_progs_arr = NULL;
102static char exclude_progs_counter = 0;
103static char *args;
104static char *input_filename = NULL;
105static regex_t re_args;
106static char *fmt;
107static char *fails;
108static char tmp[MAX_INPUT_BUFFER];
109static int kthread_filter = 0;
110static int usepid = 0; /* whether to test for pid or /proc/pid/exe */
111
112static int
113stat_exe (const pid_t pid, struct stat *buf) {
114 char *path; 87 char *path;
115 int ret;
116 xasprintf(&path, "/proc/%d/exe", pid); 88 xasprintf(&path, "/proc/%d/exe", pid);
117 ret = stat(path, buf); 89 int ret = stat(path, buf);
118 free(path); 90 free(path);
119 return ret; 91 return ret;
120} 92}
121 93
122 94int main(int argc, char **argv) {
123int 95 setlocale(LC_ALL, "");
124main (int argc, char **argv)
125{
126 char *input_buffer;
127 char *input_line;
128 char *procprog;
129
130 pid_t mypid = 0;
131 pid_t myppid = 0;
132 struct stat statbuf;
133 dev_t mydev = 0;
134 ino_t myino = 0;
135 int procuid = 0;
136 pid_t procpid = 0;
137 pid_t procppid = 0;
138 pid_t kthread_ppid = 0;
139 int procvsz = 0;
140 int procrss = 0;
141 int procseconds = 0;
142 float procpcpu = 0;
143 char procstat[8];
144 char procetime[MAX_INPUT_BUFFER] = { '\0' };
145 char *procargs;
146
147 const char *zombie = "Z";
148
149 int resultsum = 0; /* bitmask of the filter criteria met by a process */
150 int found = 0; /* counter for number of lines returned in `ps` output */
151 int procs = 0; /* counter for number of processes meeting filter criteria */
152 int pos; /* number of spaces before 'args' in `ps` output */
153 int cols; /* number of columns in ps output */
154 int expected_cols = PS_COLS - 1;
155 int warn = 0; /* number of processes in warn state */
156 int crit = 0; /* number of processes in crit state */
157 int i = 0;
158 int result = STATE_UNKNOWN;
159 int ret = 0;
160 output chld_out, chld_err;
161
162 setlocale (LC_ALL, "");
163 bindtextdomain (PACKAGE, LOCALEDIR);
164 textdomain (PACKAGE);
165 setlocale(LC_NUMERIC, "POSIX"); 96 setlocale(LC_NUMERIC, "POSIX");
166 97 bindtextdomain(PACKAGE, LOCALEDIR);
167 input_buffer = malloc (MAX_INPUT_BUFFER); 98 textdomain(PACKAGE);
168 procprog = malloc (MAX_INPUT_BUFFER);
169
170 xasprintf (&metric_name, "PROCS");
171 metric = METRIC_PROCS;
172 99
173 /* Parse extra opts if any */ 100 /* Parse extra opts if any */
174 argv=np_extra_opts (&argc, argv, progname); 101 argv = np_extra_opts(&argc, argv, progname);
102
103 check_procs_config_wrapper tmp_config = process_arguments(argc, argv);
104 if (tmp_config.errorcode == ERROR) {
105 usage4(_("Could not parse arguments"));
106 }
175 107
176 if (process_arguments (argc, argv) == ERROR) 108 check_procs_config config = tmp_config.config;
177 usage4 (_("Could not parse arguments"));
178 109
179 /* find ourself */ 110 /* find ourself */
180 mypid = getpid(); 111 pid_t mypid = getpid();
181 myppid = getppid(); 112 pid_t myppid = getppid();
182 if (usepid || stat_exe(mypid, &statbuf) == -1) { 113 dev_t mydev = 0;
114 ino_t myino = 0;
115 struct stat statbuf;
116 if (config.usepid || stat_exe(mypid, &statbuf) == -1) {
183 /* usepid might have been set by -T */ 117 /* usepid might have been set by -T */
184 usepid = 1; 118 config.usepid = true;
185 } else { 119 } else {
186 usepid = 0; 120 config.usepid = false;
187 mydev = statbuf.st_dev; 121 mydev = statbuf.st_dev;
188 myino = statbuf.st_ino; 122 myino = statbuf.st_ino;
189 } 123 }
190 124
191 /* Set signal handling and alarm timeout */ 125 /* Set signal handling and alarm timeout */
192 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 126 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
193 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 127 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
194 } 128 }
195 (void) alarm ((unsigned) timeout_interval); 129 (void)alarm(timeout_interval);
196 130
197 if (verbose >= 2) 131 if (verbose >= 2) {
198 printf (_("CMD: %s\n"), PS_COMMAND); 132 printf(_("CMD: %s\n"), PS_COMMAND);
133 }
199 134
200 if (input_filename == NULL) { 135 output chld_out;
201 result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); 136 output chld_err;
137 mp_state_enum result = STATE_UNKNOWN;
138 if (config.input_filename == NULL) {
139 result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0);
202 if (chld_err.lines > 0) { 140 if (chld_err.lines > 0) {
203 printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); 141 printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]);
204 exit(STATE_WARNING); 142 exit(STATE_WARNING);
205 } 143 }
206 } else { 144 } else {
207 result = cmd_file_read( input_filename, &chld_out, 0); 145 result = cmd_file_read(config.input_filename, &chld_out, 0);
208 } 146 }
209 147
148 int pos; /* number of spaces before 'args' in `ps` output */
149 uid_t procuid = 0;
150 pid_t procpid = 0;
151 pid_t procppid = 0;
152 pid_t kthread_ppid = 0;
153 int warn = 0; /* number of processes in warn state */
154 int crit = 0; /* number of processes in crit state */
155 int procvsz = 0;
156 int procrss = 0;
157 int procseconds = 0;
158 float procpcpu = 0;
159 char procstat[8];
160 char procetime[MAX_INPUT_BUFFER] = {'\0'};
161 int resultsum = 0; /* bitmask of the filter criteria met by a process */
162 int found = 0; /* counter for number of lines returned in `ps` output */
163 int procs = 0; /* counter for number of processes meeting filter criteria */
164 char *input_buffer = malloc(MAX_INPUT_BUFFER);
165 char *procprog = malloc(MAX_INPUT_BUFFER);
166 const int expected_cols = PS_COLS - 1;
167
210 /* flush first line: j starts at 1 */ 168 /* flush first line: j starts at 1 */
211 for (size_t j = 1; j < chld_out.lines; j++) { 169 for (size_t j = 1; j < chld_out.lines; j++) {
212 input_line = chld_out.line[j]; 170 char *input_line = chld_out.line[j];
213 171
214 if (verbose >= 3) 172 if (verbose >= 3) {
215 printf ("%s", input_line); 173 printf("%s", input_line);
174 }
216 175
217 strcpy (procprog, ""); 176 strcpy(procprog, "");
218 xasprintf (&procargs, "%s", ""); 177 char *procargs;
178 xasprintf(&procargs, "%s", "");
219 179
220 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); 180 /* number of columns in ps output */
181 int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST);
221 182
222 /* Zombie processes do not give a procprog command */ 183 /* Zombie processes do not give a procprog command */
223 if ( cols < expected_cols && strstr(procstat, zombie) ) { 184 const char *zombie = "Z";
185 if (cols < expected_cols && strstr(procstat, zombie)) {
224 cols = expected_cols; 186 cols = expected_cols;
225 } 187 }
226 if ( cols >= expected_cols ) { 188 if (cols >= expected_cols) {
227 resultsum = 0; 189 resultsum = 0;
228 xasprintf (&procargs, "%s", input_line + pos); 190 xasprintf(&procargs, "%s", input_line + pos);
229 strip (procargs); 191 strip(procargs);
230 192
231 /* Some ps return full pathname for command. This removes path */ 193 /* Some ps return full pathname for command. This removes path */
232 strcpy(procprog, base_name(procprog)); 194 strcpy(procprog, base_name(procprog));
233 195
234 /* we need to convert the elapsed time to seconds */ 196 /* we need to convert the elapsed time to seconds */
235 procseconds = convert_to_seconds(procetime); 197 procseconds = convert_to_seconds(procetime, config.metric);
236 198
237 if (verbose >= 3) 199 if (verbose >= 3) {
238 printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 200 printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
239 procs, procuid, procvsz, procrss, 201 "prog=%s args=%s\n",
240 procpid, procppid, procpcpu, procstat, 202 procs, procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat,
241 procetime, procprog, procargs); 203 procetime, procprog, procargs);
204 }
242 205
243 /* Ignore self */ 206 /* Ignore self */
244 if ((usepid && mypid == procpid) || 207 int ret = 0;
245 ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || 208 if ((config.usepid && mypid == procpid) ||
246 (ret == -1 && errno == ENOENT)) 209 (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) &&
247 ) { 210 statbuf.st_dev == mydev && statbuf.st_ino == myino)) ||
248 if (verbose >= 3) 211 (ret == -1 && errno == ENOENT))) {
249 printf("not considering - is myself or gone\n"); 212 if (verbose >= 3) {
213 printf("not considering - is myself or gone\n");
214 }
250 continue; 215 continue;
251 } 216 }
252 /* Ignore parent*/ 217 /* Ignore parent*/
253 else if (myppid == procpid) { 218 if (myppid == procpid) {
254 if (verbose >= 3) 219 if (verbose >= 3) {
255 printf("not considering - is parent\n"); 220 printf("not considering - is parent\n");
221 }
256 continue; 222 continue;
257 } 223 }
258 224
259 /* Ignore our own children */ 225 /* Ignore our own children */
260 if (procppid == mypid) { 226 if (procppid == mypid) {
261 if (verbose >= 3) 227 if (verbose >= 3) {
262 printf("not considering - is our child\n"); 228 printf("not considering - is our child\n");
229 }
263 continue; 230 continue;
264 } 231 }
265 232
266 /* Ignore excluded processes by name */ 233 /* Ignore excluded processes by name */
267 if(options & EXCLUDE_PROGS) { 234 if (config.options & EXCLUDE_PROGS) {
268 int found = 0; 235 bool found = false;
269 int i = 0; 236 for (int i = 0; i < (config.exclude_progs_counter); i++) {
270 237 if (!strcmp(procprog, config.exclude_progs_arr[i])) {
271 for(i=0; i < (exclude_progs_counter); i++) { 238 found = true;
272 if(!strcmp(procprog, exclude_progs_arr[i])) { 239 }
273 found = 1; 240 }
274 } 241 if (!found) {
275 } 242 resultsum |= EXCLUDE_PROGS;
276 if(found == 0) { 243 } else {
277 resultsum |= EXCLUDE_PROGS; 244 if (verbose >= 3) {
278 } else 245 printf("excluding - by ignorelist\n");
279 { 246 }
280 if(verbose >= 3) 247 }
281 printf("excluding - by ignorelist\n");
282 }
283 } 248 }
284 249
285 /* filter kernel threads (children of KTHREAD_PARENT)*/ 250 /* filter kernel threads (children of KTHREAD_PARENT)*/
286 /* TODO adapt for other OSes than GNU/Linux 251 /* TODO adapt for other OSes than GNU/Linux
287 sorry for not doing that, but I've no other OSes to test :-( */ 252 sorry for not doing that, but I've no other OSes to test :-( */
288 if (kthread_filter == 1) { 253 if (config.kthread_filter) {
289 /* get pid KTHREAD_PARENT */ 254 /* get pid KTHREAD_PARENT */
290 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) 255 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) {
291 kthread_ppid = procpid; 256 kthread_ppid = procpid;
257 }
292 258
293 if (kthread_ppid == procppid) { 259 if (kthread_ppid == procppid) {
294 if (verbose >= 2) 260 if (verbose >= 2) {
295 printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); 261 printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid,
262 procppid, procprog, procargs);
263 }
296 continue; 264 continue;
297 } 265 }
298 } 266 }
299 267
300 if ((options & STAT) && (strstr (procstat, statopts))) 268 if ((config.options & STAT) && (strstr(procstat, config.statopts))) {
301 resultsum |= STAT; 269 resultsum |= STAT;
302 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) 270 }
271 if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) {
303 resultsum |= ARGS; 272 resultsum |= ARGS;
304 if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) 273 }
274 if ((config.options & EREG_ARGS) && procargs &&
275 (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) {
305 resultsum |= EREG_ARGS; 276 resultsum |= EREG_ARGS;
306 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) 277 }
278 if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) {
307 resultsum |= PROG; 279 resultsum |= PROG;
308 if ((options & PPID) && (procppid == ppid)) 280 }
281 if ((config.options & PPID) && (procppid == config.ppid)) {
309 resultsum |= PPID; 282 resultsum |= PPID;
310 if ((options & USER) && (procuid == uid)) 283 }
284 if ((config.options & USER) && (procuid == config.uid)) {
311 resultsum |= USER; 285 resultsum |= USER;
312 if ((options & VSZ) && (procvsz >= vsz)) 286 }
287 if ((config.options & VSZ) && (procvsz >= config.vsz)) {
313 resultsum |= VSZ; 288 resultsum |= VSZ;
314 if ((options & RSS) && (procrss >= rss)) 289 }
290 if ((config.options & RSS) && (procrss >= config.rss)) {
315 resultsum |= RSS; 291 resultsum |= RSS;
316 if ((options & PCPU) && (procpcpu >= pcpu)) 292 }
293 if ((config.options & PCPU) && (procpcpu >= config.pcpu)) {
317 resultsum |= PCPU; 294 resultsum |= PCPU;
295 }
318 296
319 found++; 297 found++;
320 298
321 /* Next line if filters not matched */ 299 /* Next line if filters not matched */
322 if (!(options == resultsum || options == ALL)) 300 if (!(config.options == resultsum || config.options == ALL)) {
323 continue; 301 continue;
302 }
324 303
325 procs++; 304 procs++;
326 if (verbose >= 2) { 305 if (verbose >= 2) {
327 printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 306 printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s "
328 procuid, procvsz, procrss, 307 "prog=%s args=%s\n",
329 procpid, procppid, procpcpu, procstat, 308 procuid, procvsz, procrss, procpid, procppid, procpcpu, procstat, procetime,
330 procetime, procprog, procargs); 309 procprog, procargs);
331 } 310 }
332 311
333 if (metric == METRIC_VSZ) 312 mp_state_enum temporary_result = STATE_OK;
334 i = get_status ((double)procvsz, procs_thresholds); 313 if (config.metric == METRIC_VSZ) {
335 else if (metric == METRIC_RSS) 314 temporary_result = get_status((double)procvsz, config.procs_thresholds);
336 i = get_status ((double)procrss, procs_thresholds); 315 } else if (config.metric == METRIC_RSS) {
316 temporary_result = get_status((double)procrss, config.procs_thresholds);
317 }
337 /* TODO? float thresholds for --metric=CPU */ 318 /* TODO? float thresholds for --metric=CPU */
338 else if (metric == METRIC_CPU) 319 else if (config.metric == METRIC_CPU) {
339 i = get_status (procpcpu, procs_thresholds); 320 temporary_result = get_status(procpcpu, config.procs_thresholds);
340 else if (metric == METRIC_ELAPSED) 321 } else if (config.metric == METRIC_ELAPSED) {
341 i = get_status ((double)procseconds, procs_thresholds); 322 temporary_result = get_status((double)procseconds, config.procs_thresholds);
323 }
342 324
343 if (metric != METRIC_PROCS) { 325 if (config.metric != METRIC_PROCS) {
344 if (i == STATE_WARNING) { 326 if (temporary_result == STATE_WARNING) {
345 warn++; 327 warn++;
346 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 328 xasprintf(&config.fails, "%s%s%s", config.fails,
347 result = max_state (result, i); 329 (strcmp(config.fails, "") ? ", " : ""), procprog);
330 result = max_state(result, temporary_result);
348 } 331 }
349 if (i == STATE_CRITICAL) { 332 if (temporary_result == STATE_CRITICAL) {
350 crit++; 333 crit++;
351 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 334 xasprintf(&config.fails, "%s%s%s", config.fails,
352 result = max_state (result, i); 335 (strcmp(config.fails, "") ? ", " : ""), procprog);
336 result = max_state(result, temporary_result);
353 } 337 }
354 } 338 }
355 } 339 }
@@ -359,339 +343,366 @@ main (int argc, char **argv)
359 } 343 }
360 } 344 }
361 345
362 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 346 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
363 printf (_("Unable to read output\n")); 347 printf(_("Unable to read output\n"));
364 return STATE_UNKNOWN; 348 return STATE_UNKNOWN;
365 } 349 }
366 350
367 if ( result == STATE_UNKNOWN ) 351 if (result == STATE_UNKNOWN) {
368 result = STATE_OK; 352 result = STATE_OK;
353 }
369 354
370 /* Needed if procs found, but none match filter */ 355 /* Needed if procs found, but none match filter */
371 if ( metric == METRIC_PROCS ) { 356 if (config.metric == METRIC_PROCS) {
372 result = max_state (result, get_status ((double)procs, procs_thresholds) ); 357 result = max_state(result, get_status((double)procs, config.procs_thresholds));
373 } 358 }
374 359
375 if ( result == STATE_OK ) { 360 if (result == STATE_OK) {
376 printf ("%s %s: ", metric_name, _("OK")); 361 printf("%s %s: ", config.metric_name, _("OK"));
377 } else if (result == STATE_WARNING) { 362 } else if (result == STATE_WARNING) {
378 printf ("%s %s: ", metric_name, _("WARNING")); 363 printf("%s %s: ", config.metric_name, _("WARNING"));
379 if ( metric != METRIC_PROCS ) { 364 if (config.metric != METRIC_PROCS) {
380 printf (_("%d warn out of "), warn); 365 printf(_("%d warn out of "), warn);
381 } 366 }
382 } else if (result == STATE_CRITICAL) { 367 } else if (result == STATE_CRITICAL) {
383 printf ("%s %s: ", metric_name, _("CRITICAL")); 368 printf("%s %s: ", config.metric_name, _("CRITICAL"));
384 if (metric != METRIC_PROCS) { 369 if (config.metric != METRIC_PROCS) {
385 printf (_("%d crit, %d warn out of "), crit, warn); 370 printf(_("%d crit, %d warn out of "), crit, warn);
386 } 371 }
387 } 372 }
388 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); 373 printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs);
389 374
390 if (strcmp(fmt,"") != 0) { 375 if (strcmp(config.fmt, "") != 0) {
391 printf (_(" with %s"), fmt); 376 printf(_(" with %s"), config.fmt);
392 } 377 }
393 378
394 if ( verbose >= 1 && strcmp(fails,"") ) 379 if (verbose >= 1 && strcmp(config.fails, "")) {
395 printf (" [%s]", fails); 380 printf(" [%s]", config.fails);
381 }
396 382
397 if (metric == METRIC_PROCS) 383 if (config.metric == METRIC_PROCS) {
398 printf (" | procs=%d;%s;%s;0;", procs, 384 printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "",
399 warning_range ? warning_range : "", 385 config.critical_range ? config.critical_range : "");
400 critical_range ? critical_range : ""); 386 } else {
401 else 387 printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit);
402 printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); 388 }
403 389
404 printf ("\n"); 390 printf("\n");
405 return result; 391 exit(result);
406} 392}
407 393
408
409
410/* process command-line arguments */ 394/* process command-line arguments */
411int 395check_procs_config_wrapper process_arguments(int argc, char **argv) {
412process_arguments (int argc, char **argv) 396 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
413{ 397 {"critical", required_argument, 0, 'c'},
414 int c = 1; 398 {"metric", required_argument, 0, 'm'},
415 char *user; 399 {"timeout", required_argument, 0, 't'},
416 struct passwd *pw; 400 {"status", required_argument, 0, 's'},
417 int option = 0; 401 {"ppid", required_argument, 0, 'p'},
418 int err; 402 {"user", required_argument, 0, 'u'},
419 int cflags = REG_NOSUB | REG_EXTENDED; 403 {"command", required_argument, 0, 'C'},
420 char errbuf[MAX_INPUT_BUFFER]; 404 {"vsz", required_argument, 0, 'z'},
421 char *temp_string; 405 {"rss", required_argument, 0, 'r'},
422 int i=0; 406 {"pcpu", required_argument, 0, 'P'},
423 static struct option longopts[] = { 407 {"elapsed", required_argument, 0, 'e'},
424 {"warning", required_argument, 0, 'w'}, 408 {"argument-array", required_argument, 0, 'a'},
425 {"critical", required_argument, 0, 'c'}, 409 {"help", no_argument, 0, 'h'},
426 {"metric", required_argument, 0, 'm'}, 410 {"version", no_argument, 0, 'V'},
427 {"timeout", required_argument, 0, 't'}, 411 {"verbose", no_argument, 0, 'v'},
428 {"status", required_argument, 0, 's'}, 412 {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1},
429 {"ppid", required_argument, 0, 'p'}, 413 {"input-file", required_argument, 0, CHAR_MAX + 2},
430 {"user", required_argument, 0, 'u'}, 414 {"no-kthreads", required_argument, 0, 'k'},
431 {"command", required_argument, 0, 'C'}, 415 {"traditional-filter", no_argument, 0, 'T'},
432 {"vsz", required_argument, 0, 'z'}, 416 {"exclude-process", required_argument, 0, 'X'},
433 {"rss", required_argument, 0, 'r'}, 417 {0, 0, 0, 0}};
434 {"pcpu", required_argument, 0, 'P'}, 418
435 {"elapsed", required_argument, 0, 'e'}, 419 for (int index = 1; index < argc; index++) {
436 {"argument-array", required_argument, 0, 'a'}, 420 if (strcmp("-to", argv[index]) == 0) {
437 {"help", no_argument, 0, 'h'}, 421 strcpy(argv[index], "-t");
438 {"version", no_argument, 0, 'V'}, 422 }
439 {"verbose", no_argument, 0, 'v'}, 423 }
440 {"ereg-argument-array", required_argument, 0, CHAR_MAX+1},
441 {"input-file", required_argument, 0, CHAR_MAX+2},
442 {"no-kthreads", required_argument, 0, 'k'},
443 {"traditional-filter", no_argument, 0, 'T'},
444 {"exclude-process", required_argument, 0, 'X'},
445 {0, 0, 0, 0}
446 };
447 424
448 for (c = 1; c < argc; c++) 425 check_procs_config_wrapper result = {
449 if (strcmp ("-to", argv[c]) == 0) 426 .errorcode = OK,
450 strcpy (argv[c], "-t"); 427 .config = check_procs_config_init(),
428 };
451 429
452 while (1) { 430 while (true) {
453 c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", 431 int option = 0;
454 longopts, &option); 432 int option_index =
433 getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option);
455 434
456 if (c == -1 || c == EOF) 435 if (option_index == -1 || option_index == EOF) {
457 break; 436 break;
437 }
458 438
459 switch (c) { 439 switch (option_index) {
460 case '?': /* help */ 440 case '?': /* help */
461 usage5 (); 441 usage5();
462 case 'h': /* help */ 442 case 'h': /* help */
463 print_help (); 443 print_help();
464 exit (STATE_UNKNOWN); 444 exit(STATE_UNKNOWN);
465 case 'V': /* version */ 445 case 'V': /* version */
466 print_revision (progname, NP_VERSION); 446 print_revision(progname, NP_VERSION);
467 exit (STATE_UNKNOWN); 447 exit(STATE_UNKNOWN);
468 case 't': /* timeout period */ 448 case 't': /* timeout period */
469 if (!is_integer (optarg)) 449 if (!is_integer(optarg)) {
470 usage2 (_("Timeout interval must be a positive integer"), optarg); 450 usage2(_("Timeout interval must be a positive integer"), optarg);
471 else 451 } else {
472 timeout_interval = atoi (optarg); 452 timeout_interval = atoi(optarg);
453 }
473 break; 454 break;
474 case 'c': /* critical threshold */ 455 case 'c': /* critical threshold */
475 critical_range = optarg; 456 result.config.critical_range = optarg;
476 break; 457 break;
477 case 'w': /* warning threshold */ 458 case 'w': /* warning threshold */
478 warning_range = optarg; 459 result.config.warning_range = optarg;
479 break; 460 break;
480 case 'p': /* process id */ 461 case 'p': { /* process id */
481 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { 462 static char tmp[MAX_INPUT_BUFFER];
482 xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); 463 if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) {
483 options |= PPID; 464 xasprintf(&result.config.fmt, "%s%sPPID = %d",
465 (result.config.fmt ? result.config.fmt : ""),
466 (result.config.options ? ", " : ""), result.config.ppid);
467 result.config.options |= PPID;
484 break; 468 break;
485 } 469 }
486 usage4 (_("Parent Process ID must be an integer!")); 470 usage4(_("Parent Process ID must be an integer!"));
487 case 's': /* status */ 471 }
488 if (statopts) 472 case 's': /* status */
473 if (result.config.statopts) {
489 break; 474 break;
490 else 475 } else {
491 statopts = optarg; 476 result.config.statopts = optarg;
492 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 477 }
493 options |= STAT; 478 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"),
479 (result.config.fmt ? result.config.fmt : ""),
480 (result.config.options ? ", " : ""), result.config.statopts);
481 result.config.options |= STAT;
494 break; 482 break;
495 case 'u': /* user or user id */ 483 case 'u': /* user or user id */ {
496 if (is_integer (optarg)) { 484 struct passwd *pw;
497 uid = atoi (optarg); 485 if (is_integer(optarg)) {
498 pw = getpwuid ((uid_t) uid); 486 result.config.uid = atoi(optarg);
487 pw = getpwuid(result.config.uid);
499 /* check to be sure user exists */ 488 /* check to be sure user exists */
500 if (pw == NULL) 489 if (pw == NULL) {
501 usage2 (_("UID was not found"), optarg); 490 usage2(_("UID was not found"), optarg);
502 } 491 }
503 else { 492 } else {
504 pw = getpwnam (optarg); 493 pw = getpwnam(optarg);
505 /* check to be sure user exists */ 494 /* check to be sure user exists */
506 if (pw == NULL) 495 if (pw == NULL) {
507 usage2 (_("User name was not found"), optarg); 496 usage2(_("User name was not found"), optarg);
497 }
508 /* then get uid */ 498 /* then get uid */
509 uid = pw->pw_uid; 499 result.config.uid = pw->pw_uid;
510 } 500 }
511 user = pw->pw_name; 501
512 xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), 502 char *user = pw->pw_name;
513 uid, user); 503 xasprintf(&result.config.fmt, "%s%sUID = %d (%s)",
514 options |= USER; 504 (result.config.fmt ? result.config.fmt : ""),
515 break; 505 (result.config.options ? ", " : ""), result.config.uid, user);
516 case 'C': /* command */ 506 result.config.options |= USER;
507 } break;
508 case 'C': /* command */
517 /* TODO: allow this to be passed in with --metric */ 509 /* TODO: allow this to be passed in with --metric */
518 if (prog) 510 if (result.config.prog) {
519 break; 511 break;
520 else 512 } else {
521 prog = optarg; 513 result.config.prog = optarg;
522 xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 514 }
523 prog); 515 xasprintf(&result.config.fmt, _("%s%scommand name '%s'"),
524 options |= PROG; 516 (result.config.fmt ? result.config.fmt : ""),
517 (result.config.options ? ", " : ""), result.config.prog);
518 result.config.options |= PROG;
525 break; 519 break;
526 case 'X': 520 case 'X':
527 if(exclude_progs) 521 if (result.config.exclude_progs) {
528 break; 522 break;
529 else 523 } else {
530 exclude_progs = optarg; 524 result.config.exclude_progs = optarg;
531 xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 525 }
532 exclude_progs); 526 xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"),
533 char *p = strtok(exclude_progs, ","); 527 (result.config.fmt ? result.config.fmt : ""),
534 528 (result.config.options ? ", " : ""), result.config.exclude_progs);
535 while(p){ 529 char *tmp_pointer = strtok(result.config.exclude_progs, ",");
536 exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); 530
537 exclude_progs_arr[exclude_progs_counter-1] = p; 531 while (tmp_pointer) {
538 p = strtok(NULL, ","); 532 result.config.exclude_progs_arr =
533 realloc(result.config.exclude_progs_arr,
534 sizeof(char *) * ++result.config.exclude_progs_counter);
535 result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] =
536 tmp_pointer;
537 tmp_pointer = strtok(NULL, ",");
539 } 538 }
540 539
541 options |= EXCLUDE_PROGS; 540 result.config.options |= EXCLUDE_PROGS;
542 break; 541 break;
543 case 'a': /* args (full path name with args) */ 542 case 'a': /* args (full path name with args) */
544 /* TODO: allow this to be passed in with --metric */ 543 /* TODO: allow this to be passed in with --metric */
545 if (args) 544 if (result.config.args) {
546 break; 545 break;
547 else 546 } else {
548 args = optarg; 547 result.config.args = optarg;
549 xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); 548 }
550 options |= ARGS; 549 xasprintf(&result.config.fmt, "%s%sargs '%s'",
550 (result.config.fmt ? result.config.fmt : ""),
551 (result.config.options ? ", " : ""), result.config.args);
552 result.config.options |= ARGS;
551 break; 553 break;
552 case CHAR_MAX+1: 554 case CHAR_MAX + 1: {
553 err = regcomp(&re_args, optarg, cflags); 555 int cflags = REG_NOSUB | REG_EXTENDED;
556 int err = regcomp(&result.config.re_args, optarg, cflags);
554 if (err != 0) { 557 if (err != 0) {
555 regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); 558 char errbuf[MAX_INPUT_BUFFER];
556 die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 559 regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER);
560 die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"),
561 _("Could not compile regular expression"), errbuf);
557 } 562 }
558 /* Strip off any | within the regex optarg */ 563 /* Strip off any | within the regex optarg */
559 temp_string = strdup(optarg); 564 char *temp_string = strdup(optarg);
560 while(temp_string[i]!='\0'){ 565 int index = 0;
561 if(temp_string[i]=='|') 566 while (temp_string[index] != '\0') {
562 temp_string[i]=','; 567 if (temp_string[index] == '|') {
563 i++; 568 temp_string[index] = ',';
564 } 569 }
565 xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); 570 index++;
566 options |= EREG_ARGS; 571 }
567 break; 572 xasprintf(&result.config.fmt, "%s%sregex args '%s'",
568 case 'r': /* RSS */ 573 (result.config.fmt ? result.config.fmt : ""),
569 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { 574 (result.config.options ? ", " : ""), temp_string);
570 xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); 575 result.config.options |= EREG_ARGS;
571 options |= RSS; 576 } break;
577 case 'r': { /* RSS */
578 static char tmp[MAX_INPUT_BUFFER];
579 if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) {
580 xasprintf(&result.config.fmt, "%s%sRSS >= %d",
581 (result.config.fmt ? result.config.fmt : ""),
582 (result.config.options ? ", " : ""), result.config.rss);
583 result.config.options |= RSS;
572 break; 584 break;
573 } 585 }
574 usage4 (_("RSS must be an integer!")); 586 usage4(_("RSS must be an integer!"));
575 case 'z': /* VSZ */ 587 }
576 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { 588 case 'z': { /* VSZ */
577 xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); 589 static char tmp[MAX_INPUT_BUFFER];
578 options |= VSZ; 590 if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) {
591 xasprintf(&result.config.fmt, "%s%sVSZ >= %d",
592 (result.config.fmt ? result.config.fmt : ""),
593 (result.config.options ? ", " : ""), result.config.vsz);
594 result.config.options |= VSZ;
579 break; 595 break;
580 } 596 }
581 usage4 (_("VSZ must be an integer!")); 597 usage4(_("VSZ must be an integer!"));
582 case 'P': /* PCPU */ 598 }
599 case 'P': { /* PCPU */
583 /* TODO: -P 1.5.5 is accepted */ 600 /* TODO: -P 1.5.5 is accepted */
584 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { 601 static char tmp[MAX_INPUT_BUFFER];
585 xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); 602 if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) {
586 options |= PCPU; 603 xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f",
604 (result.config.fmt ? result.config.fmt : ""),
605 (result.config.options ? ", " : ""), result.config.pcpu);
606 result.config.options |= PCPU;
587 break; 607 break;
588 } 608 }
589 usage4 (_("PCPU must be a float!")); 609 usage4(_("PCPU must be a float!"));
610 }
590 case 'm': 611 case 'm':
591 xasprintf (&metric_name, "%s", optarg); 612 xasprintf(&result.config.metric_name, "%s", optarg);
592 if ( strcmp(optarg, "PROCS") == 0) { 613 if (strcmp(optarg, "PROCS") == 0) {
593 metric = METRIC_PROCS; 614 result.config.metric = METRIC_PROCS;
594 break; 615 break;
595 } 616 }
596 else if ( strcmp(optarg, "VSZ") == 0) { 617 if (strcmp(optarg, "VSZ") == 0) {
597 metric = METRIC_VSZ; 618 result.config.metric = METRIC_VSZ;
598 break; 619 break;
599 } 620 }
600 else if ( strcmp(optarg, "RSS") == 0 ) { 621 if (strcmp(optarg, "RSS") == 0) {
601 metric = METRIC_RSS; 622 result.config.metric = METRIC_RSS;
602 break; 623 break;
603 } 624 }
604 else if ( strcmp(optarg, "CPU") == 0 ) { 625 if (strcmp(optarg, "CPU") == 0) {
605 metric = METRIC_CPU; 626 result.config.metric = METRIC_CPU;
606 break; 627 break;
607 } 628 }
608 else if ( strcmp(optarg, "ELAPSED") == 0) { 629 if (strcmp(optarg, "ELAPSED") == 0) {
609 metric = METRIC_ELAPSED; 630 result.config.metric = METRIC_ELAPSED;
610 break; 631 break;
611 } 632 }
612 633
613 usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); 634 usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!"));
614 case 'k': /* linux kernel thread filter */ 635 case 'k': /* linux kernel thread filter */
615 kthread_filter = 1; 636 result.config.kthread_filter = true;
616 break; 637 break;
617 case 'v': /* command */ 638 case 'v': /* command */
618 verbose++; 639 verbose++;
619 break; 640 break;
620 case 'T': 641 case 'T':
621 usepid = 1; 642 result.config.usepid = true;
622 break; 643 break;
623 case CHAR_MAX+2: 644 case CHAR_MAX + 2:
624 input_filename = optarg; 645 result.config.input_filename = optarg;
625 break; 646 break;
626 } 647 }
627 } 648 }
628 649
629 c = optind; 650 int index = optind;
630 if ((! warning_range) && argv[c]) 651 if ((!result.config.warning_range) && argv[index]) {
631 warning_range = argv[c++]; 652 result.config.warning_range = argv[index++];
632 if ((! critical_range) && argv[c]) 653 }
633 critical_range = argv[c++]; 654 if ((!result.config.critical_range) && argv[index]) {
634 if (statopts == NULL && argv[c]) { 655 result.config.critical_range = argv[index++];
635 xasprintf (&statopts, "%s", argv[c++]); 656 }
636 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 657 if (result.config.statopts == NULL && argv[index]) {
637 options |= STAT; 658 xasprintf(&result.config.statopts, "%s", argv[index++]);
659 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"),
660 (result.config.fmt ? result.config.fmt : ""), (result.config.options ? ", " : ""),
661 result.config.statopts);
662 result.config.options |= STAT;
638 } 663 }
639 664
640 /* this will abort in case of invalid ranges */ 665 /* this will abort in case of invalid ranges */
641 set_thresholds (&procs_thresholds, warning_range, critical_range); 666 set_thresholds(&result.config.procs_thresholds, result.config.warning_range,
667 result.config.critical_range);
642 668
643 return validate_arguments (); 669 return validate_arguments(result);
644} 670}
645 671
672check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) {
673 if (config_wrapper.config.options == 0) {
674 config_wrapper.config.options = ALL;
675 }
646 676
677 if (config_wrapper.config.statopts == NULL) {
678 config_wrapper.config.statopts = strdup("");
679 }
647 680
648int 681 if (config_wrapper.config.prog == NULL) {
649validate_arguments () 682 config_wrapper.config.prog = strdup("");
650{ 683 }
651 if (options == 0)
652 options = ALL;
653
654 if (statopts==NULL)
655 statopts = strdup("");
656
657 if (prog==NULL)
658 prog = strdup("");
659 684
660 if (args==NULL) 685 if (config_wrapper.config.args == NULL) {
661 args = strdup(""); 686 config_wrapper.config.args = strdup("");
687 }
662 688
663 if (fmt==NULL) 689 if (config_wrapper.config.fmt == NULL) {
664 fmt = strdup(""); 690 config_wrapper.config.fmt = strdup("");
691 }
665 692
666 if (fails==NULL) 693 if (config_wrapper.config.fails == NULL) {
667 fails = strdup(""); 694 config_wrapper.config.fails = strdup("");
695 }
668 696
669 return options; 697 // return options;
698 return config_wrapper;
670} 699}
671 700
672
673/* convert the elapsed time to seconds */ 701/* convert the elapsed time to seconds */
674int 702int convert_to_seconds(char *etime, enum metric metric) {
675convert_to_seconds(char *etime) { 703 int hyphcnt = 0;
676 704 int coloncnt = 0;
677 char *ptr; 705 for (char *ptr = etime; *ptr != '\0'; ptr++) {
678 int total;
679
680 int hyphcnt;
681 int coloncnt;
682 int days;
683 int hours;
684 int minutes;
685 int seconds;
686
687 hyphcnt = 0;
688 coloncnt = 0;
689 days = 0;
690 hours = 0;
691 minutes = 0;
692 seconds = 0;
693
694 for (ptr = etime; *ptr != '\0'; ptr++) {
695 706
696 if (*ptr == '-') { 707 if (*ptr == '-') {
697 hyphcnt++; 708 hyphcnt++;
@@ -703,9 +714,12 @@ convert_to_seconds(char *etime) {
703 } 714 }
704 } 715 }
705 716
717 int days = 0;
718 int hours = 0;
719 int minutes = 0;
720 int seconds = 0;
706 if (hyphcnt > 0) { 721 if (hyphcnt > 0) {
707 sscanf(etime, "%d-%d:%d:%d", 722 sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds);
708 &days, &hours, &minutes, &seconds);
709 /* linux 2.6.5/2.6.6 reporting some processes with infinite 723 /* linux 2.6.5/2.6.6 reporting some processes with infinite
710 * elapsed times for some reason */ 724 * elapsed times for some reason */
711 if (days == 49710) { 725 if (days == 49710) {
@@ -713,135 +727,129 @@ convert_to_seconds(char *etime) {
713 } 727 }
714 } else { 728 } else {
715 if (coloncnt == 2) { 729 if (coloncnt == 2) {
716 sscanf(etime, "%d:%d:%d", 730 sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds);
717 &hours, &minutes, &seconds);
718 } else if (coloncnt == 1) { 731 } else if (coloncnt == 1) {
719 sscanf(etime, "%d:%d", 732 sscanf(etime, "%d:%d", &minutes, &seconds);
720 &minutes, &seconds);
721 } 733 }
722 } 734 }
723 735
724 total = (days * 86400) + 736 int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds;
725 (hours * 3600) +
726 (minutes * 60) +
727 seconds;
728 737
729 if (verbose >= 3 && metric == METRIC_ELAPSED) { 738 if (verbose >= 3 && metric == METRIC_ELAPSED) {
730 printf("seconds: %d\n", total); 739 printf("seconds: %d\n", total);
731 } 740 }
732 return total; 741 return total;
733} 742}
734 743
735 744void print_help(void) {
736void 745 print_revision(progname, NP_VERSION);
737print_help (void) 746
738{ 747 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
739 print_revision (progname, NP_VERSION); 748 printf(COPYRIGHT, copyright, email);
740 749
741 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 750 printf("%s\n",
742 printf (COPYRIGHT, copyright, email); 751 _("Checks all processes and generates WARNING or CRITICAL states if the specified"));
743 752 printf("%s\n",
744 printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); 753 _("metric is outside the required threshold ranges. The metric defaults to number"));
745 printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); 754 printf("%s\n",
746 printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); 755 _("of processes. Search filters can be applied to limit the processes to check."));
747 756
748 printf ("\n\n"); 757 printf("\n\n");
749 758
750 printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); 759 printf("%s\n",
751 printf ("%s\n", _("are excluded from any checks to prevent false positives.")); 760 _("The parent process, check_procs itself and any child process of check_procs (ps)"));
752 761 printf("%s\n", _("are excluded from any checks to prevent false positives."));
753 printf ("\n\n"); 762
754 763 printf("\n\n");
755 print_usage (); 764
756 765 print_usage();
757 printf (UT_HELP_VRSN); 766
758 printf (UT_EXTRA_OPTS); 767 printf(UT_HELP_VRSN);
759 printf (" %s\n", "-w, --warning=RANGE"); 768 printf(UT_EXTRA_OPTS);
760 printf (" %s\n", _("Generate warning state if metric is outside this range")); 769 printf(" %s\n", "-w, --warning=RANGE");
761 printf (" %s\n", "-c, --critical=RANGE"); 770 printf(" %s\n", _("Generate warning state if metric is outside this range"));
762 printf (" %s\n", _("Generate critical state if metric is outside this range")); 771 printf(" %s\n", "-c, --critical=RANGE");
763 printf (" %s\n", "-m, --metric=TYPE"); 772 printf(" %s\n", _("Generate critical state if metric is outside this range"));
764 printf (" %s\n", _("Check thresholds against metric. Valid types:")); 773 printf(" %s\n", "-m, --metric=TYPE");
765 printf (" %s\n", _("PROCS - number of processes (default)")); 774 printf(" %s\n", _("Check thresholds against metric. Valid types:"));
766 printf (" %s\n", _("VSZ - virtual memory size")); 775 printf(" %s\n", _("PROCS - number of processes (default)"));
767 printf (" %s\n", _("RSS - resident set memory size")); 776 printf(" %s\n", _("VSZ - virtual memory size"));
768 printf (" %s\n", _("CPU - percentage CPU")); 777 printf(" %s\n", _("RSS - resident set memory size"));
778 printf(" %s\n", _("CPU - percentage CPU"));
769/* only linux etime is support currently */ 779/* only linux etime is support currently */
770#if defined( __linux__ ) 780#if defined(__linux__)
771 printf (" %s\n", _("ELAPSED - time elapsed in seconds")); 781 printf(" %s\n", _("ELAPSED - time elapsed in seconds"));
772#endif /* defined(__linux__) */ 782#endif /* defined(__linux__) */
773 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 783 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
774 784
775 printf (" %s\n", "-v, --verbose"); 785 printf(" %s\n", "-v, --verbose");
776 printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); 786 printf(" %s\n", _("Extra information. Up to 3 verbosity levels"));
777 787
778 printf (" %s\n", "-T, --traditional"); 788 printf(" %s\n", "-T, --traditional");
779 printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); 789 printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe"));
780 790
781 printf ("\n"); 791 printf("\n");
782 printf ("%s\n", "Filters:"); 792 printf("%s\n", "Filters:");
783 printf (" %s\n", "-s, --state=STATUSFLAGS"); 793 printf(" %s\n", "-s, --state=STATUSFLAGS");
784 printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); 794 printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or"));
785 printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); 795 printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,"));
786 printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); 796 printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command)."));
787 printf (" %s\n", "-p, --ppid=PPID"); 797 printf(" %s\n", "-p, --ppid=PPID");
788 printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); 798 printf(" %s\n", _("Only scan for children of the parent process ID indicated."));
789 printf (" %s\n", "-z, --vsz=VSZ"); 799 printf(" %s\n", "-z, --vsz=VSZ");
790 printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); 800 printf(" %s\n", _("Only scan for processes with VSZ higher than indicated."));
791 printf (" %s\n", "-r, --rss=RSS"); 801 printf(" %s\n", "-r, --rss=RSS");
792 printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); 802 printf(" %s\n", _("Only scan for processes with RSS higher than indicated."));
793 printf (" %s\n", "-P, --pcpu=PCPU"); 803 printf(" %s\n", "-P, --pcpu=PCPU");
794 printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); 804 printf(" %s\n", _("Only scan for processes with PCPU higher than indicated."));
795 printf (" %s\n", "-u, --user=USER"); 805 printf(" %s\n", "-u, --user=USER");
796 printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); 806 printf(" %s\n", _("Only scan for processes with user name or ID indicated."));
797 printf (" %s\n", "-a, --argument-array=STRING"); 807 printf(" %s\n", "-a, --argument-array=STRING");
798 printf (" %s\n", _("Only scan for processes with args that contain STRING.")); 808 printf(" %s\n", _("Only scan for processes with args that contain STRING."));
799 printf (" %s\n", "--ereg-argument-array=STRING"); 809 printf(" %s\n", "--ereg-argument-array=STRING");
800 printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); 810 printf(" %s\n", _("Only scan for processes with args that contain the regex STRING."));
801 printf (" %s\n", "-C, --command=COMMAND"); 811 printf(" %s\n", "-C, --command=COMMAND");
802 printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); 812 printf(" %s\n", _("Only scan for exact matches of COMMAND (without path)."));
803 printf (" %s\n", "-X, --exclude-process"); 813 printf(" %s\n", "-X, --exclude-process");
804 printf (" %s\n", _("Exclude processes which match this comma separated list")); 814 printf(" %s\n", _("Exclude processes which match this comma separated list"));
805 printf (" %s\n", "-k, --no-kthreads"); 815 printf(" %s\n", "-k, --no-kthreads");
806 printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); 816 printf(" %s\n", _("Only scan for non kernel threads (works on Linux only)."));
807 817
808 printf(_("\n\ 818 printf(_("\n\
809RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ 819RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
810specified 'max:min', a warning status will be generated if the\n\ 820specified 'max:min', a warning status will be generated if the\n\
811count is inside the specified range\n\n")); 821count is inside the specified range\n\n"));
812 822
813 printf(_("\ 823 printf(_("\
814This plugin checks the number of currently running processes and\n\ 824This plugin checks the number of currently running processes and\n\
815generates WARNING or CRITICAL states if the process count is outside\n\ 825generates WARNING or CRITICAL states if the process count is outside\n\
816the specified threshold ranges. The process count can be filtered by\n\ 826the specified threshold ranges. The process count can be filtered by\n\
817process owner, parent process PID, current state (e.g., 'Z'), or may\n\ 827process owner, parent process PID, current state (e.g., 'Z'), or may\n\
818be the total number of running processes\n\n")); 828be the total number of running processes\n\n"));
819 829
820 printf ("%s\n", _("Examples:")); 830 printf("%s\n", _("Examples:"));
821 printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); 831 printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry");
822 printf (" %s\n", _("Warning if not two processes with command name portsentry.")); 832 printf(" %s\n", _("Warning if not two processes with command name portsentry."));
823 printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); 833 printf(" %s\n\n", _("Critical if < 2 or > 1024 processes"));
824 printf (" %s\n", "check_procs -c 1: -C sshd"); 834 printf(" %s\n", "check_procs -c 1: -C sshd");
825 printf (" %s\n", _("Critical if not at least 1 process with command sshd")); 835 printf(" %s\n", _("Critical if not at least 1 process with command sshd"));
826 printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); 836 printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd");
827 printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); 837 printf(" %s\n", _("Warning if > 1024 processes with command name sshd."));
828 printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); 838 printf(" %s\n\n", _("Critical if < 1 processes with command name sshd."));
829 printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); 839 printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root");
830 printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); 840 printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing"));
831 printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); 841 printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root"));
832 printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); 842 printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ");
833 printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); 843 printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K"));
834 printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); 844 printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU");
835 printf (" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); 845 printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%"));
836 846
837 printf (UT_SUPPORT); 847 printf(UT_SUPPORT);
838} 848}
839 849
840void 850void print_usage(void) {
841print_usage (void) 851 printf("%s\n", _("Usage:"));
842{ 852 printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname);
843 printf ("%s\n", _("Usage:")); 853 printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
844 printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); 854 printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
845 printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
846 printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
847} 855}
diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h
new file mode 100644
index 00000000..e32ca066
--- /dev/null
+++ b/plugins/check_procs.d/config.h
@@ -0,0 +1,75 @@
1#pragma once
2
3#include "../../config.h"
4#include "regex.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <string.h>
8#include <sys/types.h>
9
10enum metric {
11 METRIC_PROCS,
12 METRIC_VSZ,
13 METRIC_RSS,
14 METRIC_CPU,
15 METRIC_ELAPSED
16};
17
18typedef struct {
19 int options; /* bitmask of filter criteria to test against */
20 enum metric metric;
21 char *metric_name;
22 char *input_filename;
23 char *prog;
24 char *args;
25 char *fmt;
26 char *fails;
27 char *exclude_progs;
28 char **exclude_progs_arr;
29 char exclude_progs_counter;
30 regex_t re_args;
31
32 bool kthread_filter;
33 bool usepid; /* whether to test for pid or /proc/pid/exe */
34 uid_t uid;
35 pid_t ppid;
36 int vsz;
37 int rss;
38 float pcpu;
39 char *statopts;
40
41 char *warning_range;
42 char *critical_range;
43 thresholds *procs_thresholds;
44} check_procs_config;
45
46check_procs_config check_procs_config_init() {
47 check_procs_config tmp = {
48 .options = 0,
49 .metric = METRIC_PROCS,
50 .metric_name = strdup("PROCS"),
51 .input_filename = NULL,
52 .prog = NULL,
53 .args = NULL,
54 .fmt = NULL,
55 .fails = NULL,
56 .exclude_progs = NULL,
57 .exclude_progs_arr = NULL,
58 .exclude_progs_counter = 0,
59 .re_args = {0},
60
61 .kthread_filter = false,
62 .usepid = false,
63 .uid = 0,
64 .ppid = 0,
65 .vsz = 0,
66 .rss = 0,
67 .pcpu = 0,
68 .statopts = NULL,
69
70 .warning_range = NULL,
71 .critical_range = NULL,
72 .procs_thresholds = NULL,
73 };
74 return tmp;
75}
diff --git a/plugins/check_radius.c b/plugins/check_radius.c
index d9ff8fa7..d26f7cf3 100644
--- a/plugins/check_radius.c
+++ b/plugins/check_radius.c
@@ -1,32 +1,32 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_radius plugin 3 * Monitoring check_radius plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_radius plugin 10 * This file contains the check_radius plugin
11* 11 *
12* Tests to see if a radius server is accepting connections. 12 * Tests to see if a radius server is accepting connections.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_radius"; 31const char *progname = "check_radius";
32const char *copyright = "2000-2024"; 32const char *copyright = "2000-2024";
@@ -35,64 +35,58 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 35#include "common.h"
36#include "utils.h" 36#include "utils.h"
37#include "netutils.h" 37#include "netutils.h"
38#include "states.h"
39#include "check_radius.d/config.h"
38 40
39#if defined(HAVE_LIBRADCLI) 41#if defined(HAVE_LIBRADCLI)
40#include <radcli/radcli.h> 42# include <radcli/radcli.h>
41#elif defined(HAVE_LIBFREERADIUS_CLIENT) 43#elif defined(HAVE_LIBFREERADIUS_CLIENT)
42#include <freeradius-client.h> 44# include <freeradius-client.h>
43#elif defined(HAVE_LIBRADIUSCLIENT_NG) 45#elif defined(HAVE_LIBRADIUSCLIENT_NG)
44#include <radiusclient-ng.h> 46# include <radiusclient-ng.h>
45#else 47#else
46#include <radiusclient.h> 48# include <radiusclient.h>
47#endif 49#endif
48 50
49static int process_arguments (int /*argc*/, char ** /*argv*/); 51typedef struct {
50static void print_help (void); 52 int errorcode;
51void print_usage (void); 53 check_radius_config config;
52 54} check_radius_config_wrapper;
53#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI) 55static check_radius_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
54#define my_rc_conf_str(a) rc_conf_str(rch,a) 56static void print_help(void);
55#if defined(HAVE_LIBRADCLI) 57void print_usage(void);
56#define my_rc_send_server(a,b) rc_send_server(rch,a,b,AUTH) 58
57#else 59#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
58#define my_rc_send_server(a,b) rc_send_server(rch,a,b) 60 defined(HAVE_LIBRADCLI)
59#endif 61# define my_rc_conf_str(a) rc_conf_str(rch, a)
60#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI) 62# if defined(HAVE_LIBRADCLI)
61#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,(a)->secret,e,f) 63# define my_rc_send_server(a, b) rc_send_server(rch, a, b, AUTH)
64# else
65# define my_rc_send_server(a, b) rc_send_server(rch, a, b)
66# endif
67# if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADCLI)
68# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, (a)->secret, e, f)
69# else
70# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(rch, a, b, c, d, e, f)
71# endif
72# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(rch, a, b, c, -1, d)
73# define my_rc_read_dictionary(a) rc_read_dictionary(rch, a)
62#else 74#else
63#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(rch,a,b,c,d,e,f) 75# define my_rc_conf_str(a) rc_conf_str(a)
64#endif 76# define my_rc_send_server(a, b) rc_send_server(a, b)
65#define my_rc_avpair_add(a,b,c,d) rc_avpair_add(rch,a,b,c,-1,d) 77# define my_rc_buildreq(a, b, c, d, e, f) rc_buildreq(a, b, c, d, e, f)
66#define my_rc_read_dictionary(a) rc_read_dictionary(rch, a) 78# define my_rc_avpair_add(a, b, c, d) rc_avpair_add(a, b, c, d)
67#else 79# define my_rc_read_dictionary(a) rc_read_dictionary(a)
68#define my_rc_conf_str(a) rc_conf_str(a)
69#define my_rc_send_server(a,b) rc_send_server(a, b)
70#define my_rc_buildreq(a,b,c,d,e,f) rc_buildreq(a,b,c,d,e,f)
71#define my_rc_avpair_add(a,b,c,d) rc_avpair_add(a, b, c, d)
72#define my_rc_read_dictionary(a) rc_read_dictionary(a)
73#endif 80#endif
74 81
75/* REJECT_RC is only defined in some version of radiusclient. It has 82/* REJECT_RC is only defined in some version of radiusclient. It has
76 * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */ 83 * been reported from radiusclient-ng 0.5.6 on FreeBSD 7.2-RELEASE */
77#ifndef REJECT_RC 84#ifndef REJECT_RC
78#define REJECT_RC BADRESP_RC 85# define REJECT_RC BADRESP_RC
79#endif 86#endif
80 87
81static int my_rc_read_config(char * /*a*/); 88static int my_rc_read_config(char * /*a*/, rc_handle ** /*rch*/);
82
83#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
84static rc_handle *rch = NULL;
85#endif
86 89
87static char *server = NULL;
88static char *username = NULL;
89static char *password = NULL;
90static char *nasid = NULL;
91static char *nasipaddress = NULL;
92static char *expect = NULL;
93static char *config_file = NULL;
94static unsigned short port = PW_AUTH_UDP_PORT;
95static int retries = 1;
96static bool verbose = false; 90static bool verbose = false;
97 91
98/****************************************************************************** 92/******************************************************************************
@@ -148,149 +142,171 @@ Please note that all tags must be lowercase to use the DocBook XML DTD.
148-@@ 142-@@
149******************************************************************************/ 143******************************************************************************/
150 144
145int main(int argc, char **argv) {
146 setlocale(LC_ALL, "");
147 bindtextdomain(PACKAGE, LOCALEDIR);
148 textdomain(PACKAGE);
151 149
150 /* Parse extra opts if any */
151 argv = np_extra_opts(&argc, argv, progname);
152
153 check_radius_config_wrapper tmp_config = process_arguments(argc, argv);
154
155 if (tmp_config.errorcode == ERROR) {
156 usage4(_("Could not parse arguments"));
157 }
158
159 check_radius_config config = tmp_config.config;
160
161#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
162 defined(HAVE_LIBRADCLI)
163 rc_handle *rch = NULL;
164#endif
165
166 char *str = strdup("dictionary");
167 if ((config.config_file && my_rc_read_config(config.config_file, &rch)) ||
168 my_rc_read_dictionary(my_rc_conf_str(str))) {
169 die(STATE_UNKNOWN, _("Config file error\n"));
170 }
171
172 uint32_t service = PW_AUTHENTICATE_ONLY;
173
174 SEND_DATA data;
175 memset(&data, 0, sizeof(data));
176 if (!(my_rc_avpair_add(&data.send_pairs, PW_SERVICE_TYPE, &service, 0) &&
177 my_rc_avpair_add(&data.send_pairs, PW_USER_NAME, config.username, 0) &&
178 my_rc_avpair_add(&data.send_pairs, PW_USER_PASSWORD, config.password, 0))) {
179 die(STATE_UNKNOWN, _("Out of Memory?\n"));
180 }
181
182 if (config.nas_id != NULL) {
183 if (!(my_rc_avpair_add(&data.send_pairs, PW_NAS_IDENTIFIER, config.nas_id, 0))) {
184 die(STATE_UNKNOWN, _("Invalid NAS-Identifier\n"));
185 }
186 }
152 187
153int
154main (int argc, char **argv)
155{
156 struct sockaddr_storage ss;
157 char name[HOST_NAME_MAX]; 188 char name[HOST_NAME_MAX];
189 if (config.nas_ip_address == NULL) {
190 if (gethostname(name, sizeof(name)) != 0) {
191 die(STATE_UNKNOWN, _("gethostname() failed!\n"));
192 }
193 config.nas_ip_address = name;
194 }
195
196 struct sockaddr_storage radius_server_socket;
197 if (!dns_lookup(config.nas_ip_address, &radius_server_socket, AF_UNSPEC)) {
198 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
199 }
200
201 uint32_t client_id = ntohl(((struct sockaddr_in *)&radius_server_socket)->sin_addr.s_addr);
202 if (my_rc_avpair_add(&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL) {
203 die(STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
204 }
205
206 my_rc_buildreq(&data, PW_ACCESS_REQUEST, config.server, config.port, (int)timeout_interval,
207 config.retries);
208
158#ifdef RC_BUFFER_LEN 209#ifdef RC_BUFFER_LEN
159 char msg[RC_BUFFER_LEN]; 210 char msg[RC_BUFFER_LEN];
160#else 211#else
161 char msg[BUFFER_LEN]; 212 char msg[BUFFER_LEN];
162#endif 213#endif
163 SEND_DATA data;
164 int result = STATE_UNKNOWN;
165 uint32_t client_id, service;
166 char *str;
167 214
168 setlocale (LC_ALL, ""); 215 int result = my_rc_send_server(&data, msg);
169 bindtextdomain (PACKAGE, LOCALEDIR); 216 rc_avpair_free(data.send_pairs);
170 textdomain (PACKAGE); 217 if (data.receive_pairs) {
171 218 rc_avpair_free(data.receive_pairs);
172 /* Parse extra opts if any */ 219 }
173 argv=np_extra_opts (&argc, argv, progname);
174 220
175 if (process_arguments (argc, argv) == ERROR) 221 if (result == TIMEOUT_RC) {
176 usage4 (_("Could not parse arguments")); 222 printf("Timeout\n");
223 exit(STATE_CRITICAL);
224 }
177 225
178 str = strdup ("dictionary"); 226 if (result == ERROR_RC) {
179 if ((config_file && my_rc_read_config (config_file)) || 227 printf(_("Auth Error\n"));
180 my_rc_read_dictionary (my_rc_conf_str (str))) 228 exit(STATE_CRITICAL);
181 die (STATE_UNKNOWN, _("Config file error\n")); 229 }
182 230
183 service = PW_AUTHENTICATE_ONLY; 231 if (result == REJECT_RC) {
232 printf(_("Auth Failed\n"));
233 exit(STATE_WARNING);
234 }
184 235
185 memset (&data, 0, sizeof(data)); 236 if (result == BADRESP_RC) {
186 if (!(my_rc_avpair_add (&data.send_pairs, PW_SERVICE_TYPE, &service, 0) && 237 printf(_("Bad Response\n"));
187 my_rc_avpair_add (&data.send_pairs, PW_USER_NAME, username, 0) && 238 exit(STATE_WARNING);
188 my_rc_avpair_add (&data.send_pairs, PW_USER_PASSWORD, password, 0) 239 }
189 ))
190 die (STATE_UNKNOWN, _("Out of Memory?\n"));
191 240
192 if (nasid != NULL) { 241 if (config.expect && !strstr(msg, config.expect)) {
193 if (!(my_rc_avpair_add (&data.send_pairs, PW_NAS_IDENTIFIER, nasid, 0))) 242 printf("%s\n", msg);
194 die (STATE_UNKNOWN, _("Invalid NAS-Identifier\n")); 243 exit(STATE_WARNING);
195 } 244 }
196 245
197 if (nasipaddress == NULL) { 246 if (result == OK_RC) {
198 if (gethostname (name, sizeof(name)) != 0) 247 printf(_("Auth OK\n"));
199 die (STATE_UNKNOWN, _("gethostname() failed!\n")); 248 exit(STATE_OK);
200 nasipaddress = name;
201 } 249 }
202 if (!dns_lookup (nasipaddress, &ss, AF_INET)) /* TODO: Support IPv6. */ 250
203 die (STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
204 client_id = ntohl (((struct sockaddr_in *)&ss)->sin_addr.s_addr);
205 if (my_rc_avpair_add (&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) == NULL)
206 die (STATE_UNKNOWN, _("Invalid NAS-IP-Address\n"));
207
208 my_rc_buildreq (&data, PW_ACCESS_REQUEST, server, port, (int)timeout_interval,
209 retries);
210
211 result = my_rc_send_server (&data, msg);
212 rc_avpair_free (data.send_pairs);
213 if (data.receive_pairs)
214 rc_avpair_free (data.receive_pairs);
215
216 if (result == TIMEOUT_RC)
217 die (STATE_CRITICAL, _("Timeout\n"));
218 if (result == ERROR_RC)
219 die (STATE_CRITICAL, _("Auth Error\n"));
220 if (result == REJECT_RC)
221 die (STATE_WARNING, _("Auth Failed\n"));
222 if (result == BADRESP_RC)
223 die (STATE_WARNING, _("Bad Response\n"));
224 if (expect && !strstr (msg, expect))
225 die (STATE_WARNING, "%s\n", msg);
226 if (result == OK_RC)
227 die (STATE_OK, _("Auth OK\n"));
228 (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result); 251 (void)snprintf(msg, sizeof(msg), _("Unexpected result code %d"), result);
229 die (STATE_UNKNOWN, "%s\n", msg); 252 printf("%s\n", msg);
253 exit(STATE_UNKNOWN);
230} 254}
231 255
232
233
234/* process command-line arguments */ 256/* process command-line arguments */
235int 257check_radius_config_wrapper process_arguments(int argc, char **argv) {
236process_arguments (int argc, char **argv)
237{
238 int c;
239
240 int option = 0;
241 static struct option longopts[] = { 258 static struct option longopts[] = {
242 {"hostname", required_argument, 0, 'H'}, 259 {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'P'},
243 {"port", required_argument, 0, 'P'}, 260 {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'},
244 {"username", required_argument, 0, 'u'}, 261 {"nas-id", required_argument, 0, 'n'}, {"nas-ip-address", required_argument, 0, 'N'},
245 {"password", required_argument, 0, 'p'}, 262 {"filename", required_argument, 0, 'F'}, {"expect", required_argument, 0, 'e'},
246 {"nas-id", required_argument, 0, 'n'}, 263 {"retries", required_argument, 0, 'r'}, {"timeout", required_argument, 0, 't'},
247 {"nas-ip-address", required_argument, 0, 'N'}, 264 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
248 {"filename", required_argument, 0, 'F'}, 265 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
249 {"expect", required_argument, 0, 'e'}, 266
250 {"retries", required_argument, 0, 'r'}, 267 check_radius_config_wrapper result = {
251 {"timeout", required_argument, 0, 't'}, 268 .errorcode = OK,
252 {"verbose", no_argument, 0, 'v'}, 269 .config = check_radius_config_init(),
253 {"version", no_argument, 0, 'V'},
254 {"help", no_argument, 0, 'h'},
255 {0, 0, 0, 0}
256 }; 270 };
257 271
258 while (1) { 272 while (true) {
259 c = getopt_long (argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, 273 int option = 0;
260 &option); 274 int option_index = getopt_long(argc, argv, "+hVvH:P:F:u:p:n:N:t:r:e:", longopts, &option);
261 275
262 if (c == -1 || c == EOF || c == 1) 276 if (option_index == -1 || option_index == EOF || option_index == 1) {
263 break; 277 break;
278 }
264 279
265 switch (c) { 280 switch (option_index) {
266 case '?': /* print short usage statement if args not parsable */ 281 case '?': /* print short usage statement if args not parsable */
267 usage5 (); 282 usage5();
268 case 'h': /* help */ 283 case 'h': /* help */
269 print_help (); 284 print_help();
270 exit (STATE_UNKNOWN); 285 exit(STATE_UNKNOWN);
271 case 'V': /* version */ 286 case 'V': /* version */
272 print_revision (progname, NP_VERSION); 287 print_revision(progname, NP_VERSION);
273 exit (STATE_UNKNOWN); 288 exit(STATE_UNKNOWN);
274 case 'v': /* verbose mode */ 289 case 'v': /* verbose mode */
275 verbose = true; 290 verbose = true;
276 break; 291 break;
277 case 'H': /* hostname */ 292 case 'H': /* hostname */
278 if (!is_host (optarg)) { 293 if (!is_host(optarg)) {
279 usage2 (_("Invalid hostname/address"), optarg); 294 usage2(_("Invalid hostname/address"), optarg);
280 } 295 }
281 server = optarg; 296 result.config.server = optarg;
282 break; 297 break;
283 case 'P': /* port */ 298 case 'P': /* port */
284 if (is_intnonneg (optarg)) 299 if (is_intnonneg(optarg)) {
285 port = (unsigned short)atoi (optarg); 300 result.config.port = (unsigned short)atoi(optarg);
286 else 301 } else {
287 usage4 (_("Port must be a positive integer")); 302 usage4(_("Port must be a positive integer"));
303 }
288 break; 304 break;
289 case 'u': /* username */ 305 case 'u': /* username */
290 username = optarg; 306 result.config.username = optarg;
291 break; 307 break;
292 case 'p': /* password */ 308 case 'p': /* password */
293 password = strdup(optarg); 309 result.config.password = strdup(optarg);
294 310
295 /* Delete the password from process list */ 311 /* Delete the password from process list */
296 while (*optarg != '\0') { 312 while (*optarg != '\0') {
@@ -298,119 +314,118 @@ process_arguments (int argc, char **argv)
298 optarg++; 314 optarg++;
299 } 315 }
300 break; 316 break;
301 case 'n': /* nas id */ 317 case 'n': /* nas id */
302 nasid = optarg; 318 result.config.nas_id = optarg;
303 break; 319 break;
304 case 'N': /* nas ip address */ 320 case 'N': /* nas ip address */
305 nasipaddress = optarg; 321 result.config.nas_ip_address = optarg;
306 break; 322 break;
307 case 'F': /* configuration file */ 323 case 'F': /* configuration file */
308 config_file = optarg; 324 result.config.config_file = optarg;
309 break; 325 break;
310 case 'e': /* expect */ 326 case 'e': /* expect */
311 expect = optarg; 327 result.config.expect = optarg;
312 break; 328 break;
313 case 'r': /* retries */ 329 case 'r': /* retries */
314 if (is_intpos (optarg)) 330 if (is_intpos(optarg)) {
315 retries = atoi (optarg); 331 result.config.retries = atoi(optarg);
316 else 332 } else {
317 usage4 (_("Number of retries must be a positive integer")); 333 usage4(_("Number of retries must be a positive integer"));
334 }
318 break; 335 break;
319 case 't': /* timeout */ 336 case 't': /* timeout */
320 if (is_intpos (optarg)) 337 if (is_intpos(optarg)) {
321 timeout_interval = (unsigned)atoi (optarg); 338 timeout_interval = (unsigned)atoi(optarg);
322 else 339 } else {
323 usage2 (_("Timeout interval must be a positive integer"), optarg); 340 usage2(_("Timeout interval must be a positive integer"), optarg);
341 }
324 break; 342 break;
325 } 343 }
326 } 344 }
327 345
328 if (server == NULL) 346 if (result.config.server == NULL) {
329 usage4 (_("Hostname was not supplied")); 347 usage4(_("Hostname was not supplied"));
330 if (username == NULL) 348 }
331 usage4 (_("User not specified")); 349 if (result.config.username == NULL) {
332 if (password == NULL) 350 usage4(_("User not specified"));
333 usage4 (_("Password not specified")); 351 }
334 if (config_file == NULL) 352 if (result.config.password == NULL) {
335 usage4 (_("Configuration file not specified")); 353 usage4(_("Password not specified"));
354 }
355 if (result.config.config_file == NULL) {
356 usage4(_("Configuration file not specified"));
357 }
336 358
337 return OK; 359 return result;
338} 360}
339 361
340 362void print_help(void) {
341
342void
343print_help (void)
344{
345 char *myport; 363 char *myport;
346 xasprintf (&myport, "%d", PW_AUTH_UDP_PORT); 364 xasprintf(&myport, "%d", PW_AUTH_UDP_PORT);
347 365
348 print_revision (progname, NP_VERSION); 366 print_revision(progname, NP_VERSION);
349 367
350 printf ("Copyright (c) 1999 Robert August Vincent II\n"); 368 printf("Copyright (c) 1999 Robert August Vincent II\n");
351 printf (COPYRIGHT, copyright, email); 369 printf(COPYRIGHT, copyright, email);
352 370
353 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections.")); 371 printf("%s\n", _("Tests to see if a RADIUS server is accepting connections."));
354 372
355 printf ("\n\n"); 373 printf("\n\n");
356 374
357 print_usage (); 375 print_usage();
358 376
359 printf (UT_HELP_VRSN); 377 printf(UT_HELP_VRSN);
360 printf (UT_EXTRA_OPTS); 378 printf(UT_EXTRA_OPTS);
361 379
362 printf (UT_HOST_PORT, 'P', myport); 380 printf(UT_HOST_PORT, 'P', myport);
363 381
364 printf (" %s\n", "-u, --username=STRING"); 382 printf(" %s\n", "-u, --username=STRING");
365 printf (" %s\n", _("The user to authenticate")); 383 printf(" %s\n", _("The user to authenticate"));
366 printf (" %s\n", "-p, --password=STRING"); 384 printf(" %s\n", "-p, --password=STRING");
367 printf (" %s\n", _("Password for authentication (SECURITY RISK)")); 385 printf(" %s\n", _("Password for authentication (SECURITY RISK)"));
368 printf (" %s\n", "-n, --nas-id=STRING"); 386 printf(" %s\n", "-n, --nas-id=STRING");
369 printf (" %s\n", _("NAS identifier")); 387 printf(" %s\n", _("NAS identifier"));
370 printf (" %s\n", "-N, --nas-ip-address=STRING"); 388 printf(" %s\n", "-N, --nas-ip-address=STRING");
371 printf (" %s\n", _("NAS IP Address")); 389 printf(" %s\n", _("NAS IP Address"));
372 printf (" %s\n", "-F, --filename=STRING"); 390 printf(" %s\n", "-F, --filename=STRING");
373 printf (" %s\n", _("Configuration file")); 391 printf(" %s\n", _("Configuration file"));
374 printf (" %s\n", "-e, --expect=STRING"); 392 printf(" %s\n", "-e, --expect=STRING");
375 printf (" %s\n", _("Response string to expect from the server")); 393 printf(" %s\n", _("Response string to expect from the server"));
376 printf (" %s\n", "-r, --retries=INTEGER"); 394 printf(" %s\n", "-r, --retries=INTEGER");
377 printf (" %s\n", _("Number of times to retry a failed connection")); 395 printf(" %s\n", _("Number of times to retry a failed connection"));
378 396
379 printf (UT_CONN_TIMEOUT, timeout_interval); 397 printf(UT_CONN_TIMEOUT, timeout_interval);
380 398
381 printf ("\n"); 399 printf("\n");
382 printf ("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections.")); 400 printf("%s\n", _("This plugin tests a RADIUS server to see if it is accepting connections."));
383 printf ("%s\n", _("The server to test must be specified in the invocation, as well as a user")); 401 printf("%s\n", _("The server to test must be specified in the invocation, as well as a user"));
384 printf ("%s\n", _("name and password. A configuration file must be present. The format of")); 402 printf("%s\n", _("name and password. A configuration file must be present. The format of"));
385 printf ("%s\n", _("the configuration file is described in the radiusclient library sources.")); 403 printf("%s\n", _("the configuration file is described in the radiusclient library sources."));
386 printf ("%s\n", _("The password option presents a substantial security issue because the")); 404 printf("%s\n", _("The password option presents a substantial security issue because the"));
387 printf ("%s\n", _("password can possibly be determined by careful watching of the command line")); 405 printf("%s\n",
388 printf ("%s\n", _("in a process listing. This risk is exacerbated because the plugin will")); 406 _("password can possibly be determined by careful watching of the command line"));
389 printf ("%s\n", _("typically be executed at regular predictable intervals. Please be sure that")); 407 printf("%s\n", _("in a process listing. This risk is exacerbated because the plugin will"));
390 printf ("%s\n", _("the password used does not allow access to sensitive system resources.")); 408 printf("%s\n",
391 409 _("typically be executed at regular predictable intervals. Please be sure that"));
392 printf (UT_SUPPORT); 410 printf("%s\n", _("the password used does not allow access to sensitive system resources."));
411
412 printf(UT_SUPPORT);
393} 413}
394 414
395 415void print_usage(void) {
396 416 printf("%s\n", _("Usage:"));
397void 417 printf("%s -H host -F config_file -u username -p password\n\
398print_usage (void)
399{
400 printf ("%s\n", _("Usage:"));
401 printf ("%s -H host -F config_file -u username -p password\n\
402 [-P port] [-t timeout] [-r retries] [-e expect]\n\ 418 [-P port] [-t timeout] [-r retries] [-e expect]\n\
403 [-n nas-id] [-N nas-ip-addr]\n", progname); 419 [-n nas-id] [-N nas-ip-addr]\n",
420 progname);
404} 421}
405 422
406 423int my_rc_read_config(char *config_file_name, rc_handle **rch) {
407 424#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || \
408int my_rc_read_config(char * a) 425 defined(HAVE_LIBRADCLI)
409{ 426 *rch = rc_read_config(config_file_name);
410#if defined(HAVE_LIBFREERADIUS_CLIENT) || defined(HAVE_LIBRADIUSCLIENT_NG) || defined(HAVE_LIBRADCLI)
411 rch = rc_read_config(a);
412 return (rch == NULL) ? 1 : 0; 427 return (rch == NULL) ? 1 : 0;
413#else 428#else
414 return rc_read_config(a); 429 return rc_read_config(config_file_name);
415#endif 430#endif
416} 431}
diff --git a/plugins/check_radius.d/config.h b/plugins/check_radius.d/config.h
new file mode 100644
index 00000000..b27d31e7
--- /dev/null
+++ b/plugins/check_radius.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#if defined(HAVE_LIBRADCLI)
6# include <radcli/radcli.h>
7#elif defined(HAVE_LIBFREERADIUS_CLIENT)
8# include <freeradius-client.h>
9#elif defined(HAVE_LIBRADIUSCLIENT_NG)
10# include <radiusclient-ng.h>
11#else
12# include <radiusclient.h>
13#endif
14
15typedef struct {
16 char *server;
17 char *username;
18 char *password;
19 char *config_file;
20 char *nas_id;
21 char *nas_ip_address;
22 int retries;
23 unsigned short port;
24
25 char *expect;
26} check_radius_config;
27
28check_radius_config check_radius_config_init() {
29 check_radius_config tmp = {
30 .server = NULL,
31 .username = NULL,
32 .password = NULL,
33 .config_file = NULL,
34 .nas_id = NULL,
35 .nas_ip_address = NULL,
36 .retries = 1,
37 .port = PW_AUTH_UDP_PORT,
38
39 .expect = NULL,
40 };
41 return tmp;
42}
diff --git a/plugins/check_real.c b/plugins/check_real.c
index 369a88b1..66d07f8f 100644
--- a/plugins/check_real.c
+++ b/plugins/check_real.c
@@ -28,6 +28,8 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
32#include <stdio.h>
31const char *progname = "check_real"; 33const char *progname = "check_real";
32const char *copyright = "2000-2024"; 34const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
@@ -35,27 +37,20 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 37#include "common.h"
36#include "netutils.h" 38#include "netutils.h"
37#include "utils.h" 39#include "utils.h"
38 40#include "check_real.d/config.h"
39enum {
40 PORT = 554
41};
42 41
43#define EXPECT "RTSP/1." 42#define EXPECT "RTSP/1."
44#define URL "" 43#define URL ""
45 44
46static int process_arguments(int, char **); 45typedef struct {
46 int errorcode;
47 check_real_config config;
48} check_real_config_wrapper;
49static check_real_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50
47static void print_help(void); 51static void print_help(void);
48void print_usage(void); 52void print_usage(void);
49 53
50static int server_port = PORT;
51static char *server_address;
52static char *host_name;
53static char *server_url = NULL;
54static char *server_expect;
55static int warning_time = 0;
56static bool check_warning_time = false;
57static int critical_time = 0;
58static bool check_critical_time = false;
59static bool verbose = false; 54static bool verbose = false;
60 55
61int main(int argc, char **argv) { 56int main(int argc, char **argv) {
@@ -66,8 +61,12 @@ int main(int argc, char **argv) {
66 /* Parse extra opts if any */ 61 /* Parse extra opts if any */
67 argv = np_extra_opts(&argc, argv, progname); 62 argv = np_extra_opts(&argc, argv, progname);
68 63
69 if (process_arguments(argc, argv) == ERROR) 64 check_real_config_wrapper tmp_config = process_arguments(argc, argv);
65 if (tmp_config.errorcode == ERROR) {
70 usage4(_("Could not parse arguments")); 66 usage4(_("Could not parse arguments"));
67 }
68
69 const check_real_config config = tmp_config.config;
71 70
72 /* initialize alarm signal handling */ 71 /* initialize alarm signal handling */
73 signal(SIGALRM, socket_timeout_alarm_handler); 72 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -78,38 +77,52 @@ int main(int argc, char **argv) {
78 77
79 /* try to connect to the host at the given port number */ 78 /* try to connect to the host at the given port number */
80 int socket; 79 int socket;
81 if (my_tcp_connect(server_address, server_port, &socket) != STATE_OK) 80 if (my_tcp_connect(config.server_address, config.server_port, &socket) != STATE_OK) {
82 die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), server_address, server_port); 81 die(STATE_CRITICAL, _("Unable to connect to %s on port %d\n"), config.server_address,
82 config.server_port);
83 }
83 84
84 /* Part I - Server Check */ 85 /* Part I - Server Check */
85 86
86 /* send the OPTIONS request */ 87 /* send the OPTIONS request */
87 char buffer[MAX_INPUT_BUFFER]; 88 char buffer[MAX_INPUT_BUFFER];
88 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", host_name, server_port); 89 sprintf(buffer, "OPTIONS rtsp://%s:%d RTSP/1.0\r\n", config.host_name, config.server_port);
89 int result = send(socket, buffer, strlen(buffer), 0); 90 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
91 if (sent_bytes == -1) {
92 die(STATE_CRITICAL, _("Sending options to %s failed\n"), config.host_name);
93 }
90 94
91 /* send the header sync */ 95 /* send the header sync */
92 sprintf(buffer, "CSeq: 1\r\n"); 96 sprintf(buffer, "CSeq: 1\r\n");
93 result = send(socket, buffer, strlen(buffer), 0); 97 sent_bytes = send(socket, buffer, strlen(buffer), 0);
98 if (sent_bytes == -1) {
99 die(STATE_CRITICAL, _("Sending header sync to %s failed\n"), config.host_name);
100 }
94 101
95 /* send a newline so the server knows we're done with the request */ 102 /* send a newline so the server knows we're done with the request */
96 sprintf(buffer, "\r\n"); 103 sprintf(buffer, "\r\n");
97 result = send(socket, buffer, strlen(buffer), 0); 104 sent_bytes = send(socket, buffer, strlen(buffer), 0);
105 if (sent_bytes == -1) {
106 die(STATE_CRITICAL, _("Sending newline to %s failed\n"), config.host_name);
107 }
98 108
99 /* watch for the REAL connection string */ 109 /* watch for the REAL connection string */
100 result = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); 110 ssize_t received_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
101 111
102 /* return a CRITICAL status if we couldn't read any data */ 112 /* return a CRITICAL status if we couldn't read any data */
103 if (result == -1) 113 if (received_bytes == -1) {
104 die(STATE_CRITICAL, _("No data received from %s\n"), host_name); 114 die(STATE_CRITICAL, _("No data received from %s\n"), config.host_name);
115 }
105 116
117 mp_state_enum result = STATE_OK;
106 char *status_line = NULL; 118 char *status_line = NULL;
107 /* make sure we find the response we are looking for */ 119 /* make sure we find the response we are looking for */
108 if (!strstr(buffer, server_expect)) { 120 if (!strstr(buffer, config.server_expect)) {
109 if (server_port == PORT) 121 if (config.server_port == PORT) {
110 printf("%s\n", _("Invalid REAL response received from host")); 122 printf("%s\n", _("Invalid REAL response received from host"));
111 else 123 } else {
112 printf(_("Invalid REAL response received from host on port %d\n"), server_port); 124 printf(_("Invalid REAL response received from host on port %d\n"), config.server_port);
125 }
113 } else { 126 } else {
114 /* else we got the REAL string, so check the return code */ 127 /* else we got the REAL string, so check the return code */
115 128
@@ -117,69 +130,81 @@ int main(int argc, char **argv) {
117 130
118 result = STATE_OK; 131 result = STATE_OK;
119 132
120 status_line = (char *)strtok(buffer, "\n"); 133 status_line = strtok(buffer, "\n");
121 134
122 if (strstr(status_line, "200")) 135 if (strstr(status_line, "200")) {
123 result = STATE_OK; 136 result = STATE_OK;
137 }
124 138
125 /* client errors result in a warning state */ 139 /* client errors result in a warning state */
126 else if (strstr(status_line, "400")) 140 else if (strstr(status_line, "400")) {
127 result = STATE_WARNING; 141 result = STATE_WARNING;
128 else if (strstr(status_line, "401")) 142 } else if (strstr(status_line, "401")) {
129 result = STATE_WARNING; 143 result = STATE_WARNING;
130 else if (strstr(status_line, "402")) 144 } else if (strstr(status_line, "402")) {
131 result = STATE_WARNING; 145 result = STATE_WARNING;
132 else if (strstr(status_line, "403")) 146 } else if (strstr(status_line, "403")) {
133 result = STATE_WARNING; 147 result = STATE_WARNING;
134 else if (strstr(status_line, "404")) 148 } else if (strstr(status_line, "404")) {
135 result = STATE_WARNING; 149 result = STATE_WARNING;
136 150 } else if (strstr(status_line, "500")) {
137 /* server errors result in a critical state */ 151 /* server errors result in a critical state */
138 else if (strstr(status_line, "500"))
139 result = STATE_CRITICAL; 152 result = STATE_CRITICAL;
140 else if (strstr(status_line, "501")) 153 } else if (strstr(status_line, "501")) {
141 result = STATE_CRITICAL; 154 result = STATE_CRITICAL;
142 else if (strstr(status_line, "502")) 155 } else if (strstr(status_line, "502")) {
143 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
144 else if (strstr(status_line, "503")) 157 } else if (strstr(status_line, "503")) {
145 result = STATE_CRITICAL; 158 result = STATE_CRITICAL;
146 159 } else {
147 else
148 result = STATE_UNKNOWN; 160 result = STATE_UNKNOWN;
161 }
149 } 162 }
150 163
151 /* Part II - Check stream exists and is ok */ 164 /* Part II - Check stream exists and is ok */
152 if ((result == STATE_OK) && (server_url != NULL)) { 165 if ((result == STATE_OK) && (config.server_url != NULL)) {
153 166
154 /* Part I - Server Check */ 167 /* Part I - Server Check */
155 168
156 /* send the DESCRIBE request */ 169 /* send the DESCRIBE request */
157 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", host_name, server_port, server_url); 170 sprintf(buffer, "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n", config.host_name,
158 result = send(socket, buffer, strlen(buffer), 0); 171 config.server_port, config.server_url);
172
173 ssize_t sent_bytes = send(socket, buffer, strlen(buffer), 0);
174 if (sent_bytes == -1) {
175 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name);
176 }
159 177
160 /* send the header sync */ 178 /* send the header sync */
161 sprintf(buffer, "CSeq: 2\r\n"); 179 sprintf(buffer, "CSeq: 2\r\n");
162 result = send(socket, buffer, strlen(buffer), 0); 180 sent_bytes = send(socket, buffer, strlen(buffer), 0);
181 if (sent_bytes == -1) {
182 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name);
183 }
163 184
164 /* send a newline so the server knows we're done with the request */ 185 /* send a newline so the server knows we're done with the request */
165 sprintf(buffer, "\r\n"); 186 sprintf(buffer, "\r\n");
166 result = send(socket, buffer, strlen(buffer), 0); 187 sent_bytes = send(socket, buffer, strlen(buffer), 0);
188 if (sent_bytes == -1) {
189 die(STATE_CRITICAL, _("Sending DESCRIBE request to %s failed\n"), config.host_name);
190 }
167 191
168 /* watch for the REAL connection string */ 192 /* watch for the REAL connection string */
169 result = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0); 193 ssize_t recv_bytes = recv(socket, buffer, MAX_INPUT_BUFFER - 1, 0);
170 buffer[result] = '\0'; /* null terminate received buffer */ 194 if (recv_bytes == -1) {
171 195 /* return a CRITICAL status if we couldn't read any data */
172 /* return a CRITICAL status if we couldn't read any data */
173 if (result == -1) {
174 printf(_("No data received from host\n")); 196 printf(_("No data received from host\n"));
175 result = STATE_CRITICAL; 197 result = STATE_CRITICAL;
176 } else { 198 } else {
199 buffer[result] = '\0'; /* null terminate received buffer */
177 /* make sure we find the response we are looking for */ 200 /* make sure we find the response we are looking for */
178 if (!strstr(buffer, server_expect)) { 201 if (!strstr(buffer, config.server_expect)) {
179 if (server_port == PORT) 202 if (config.server_port == PORT) {
180 printf("%s\n", _("Invalid REAL response received from host")); 203 printf("%s\n", _("Invalid REAL response received from host"));
181 else 204 } else {
182 printf(_("Invalid REAL response received from host on port %d\n"), server_port); 205 printf(_("Invalid REAL response received from host on port %d\n"),
206 config.server_port);
207 }
183 } else { 208 } else {
184 209
185 /* else we got the REAL string, so check the return code */ 210 /* else we got the REAL string, so check the return code */
@@ -188,51 +213,57 @@ int main(int argc, char **argv) {
188 213
189 result = STATE_OK; 214 result = STATE_OK;
190 215
191 status_line = (char *)strtok(buffer, "\n"); 216 status_line = strtok(buffer, "\n");
192 217
193 if (strstr(status_line, "200")) 218 if (strstr(status_line, "200")) {
194 result = STATE_OK; 219 result = STATE_OK;
220 }
195 221
196 /* client errors result in a warning state */ 222 /* client errors result in a warning state */
197 else if (strstr(status_line, "400")) 223 else if (strstr(status_line, "400")) {
198 result = STATE_WARNING; 224 result = STATE_WARNING;
199 else if (strstr(status_line, "401")) 225 } else if (strstr(status_line, "401")) {
200 result = STATE_WARNING; 226 result = STATE_WARNING;
201 else if (strstr(status_line, "402")) 227 } else if (strstr(status_line, "402")) {
202 result = STATE_WARNING; 228 result = STATE_WARNING;
203 else if (strstr(status_line, "403")) 229 } else if (strstr(status_line, "403")) {
204 result = STATE_WARNING; 230 result = STATE_WARNING;
205 else if (strstr(status_line, "404")) 231 } else if (strstr(status_line, "404")) {
206 result = STATE_WARNING; 232 result = STATE_WARNING;
233 }
207 234
208 /* server errors result in a critical state */ 235 /* server errors result in a critical state */
209 else if (strstr(status_line, "500")) 236 else if (strstr(status_line, "500")) {
210 result = STATE_CRITICAL; 237 result = STATE_CRITICAL;
211 else if (strstr(status_line, "501")) 238 } else if (strstr(status_line, "501")) {
212 result = STATE_CRITICAL; 239 result = STATE_CRITICAL;
213 else if (strstr(status_line, "502")) 240 } else if (strstr(status_line, "502")) {
214 result = STATE_CRITICAL; 241 result = STATE_CRITICAL;
215 else if (strstr(status_line, "503")) 242 } else if (strstr(status_line, "503")) {
216 result = STATE_CRITICAL; 243 result = STATE_CRITICAL;
244 }
217 245
218 else 246 else {
219 result = STATE_UNKNOWN; 247 result = STATE_UNKNOWN;
248 }
220 } 249 }
221 } 250 }
222 } 251 }
223 252
224 /* Return results */ 253 /* Return results */
225 if (result == STATE_OK) { 254 if (result == STATE_OK) {
226 255 if (config.check_critical_time && (end_time - start_time) > config.critical_time) {
227 if (check_critical_time && (end_time - start_time) > critical_time)
228 result = STATE_CRITICAL; 256 result = STATE_CRITICAL;
229 else if (check_warning_time && (end_time - start_time) > warning_time) 257 } else if (config.check_warning_time && (end_time - start_time) > config.warning_time) {
230 result = STATE_WARNING; 258 result = STATE_WARNING;
259 }
231 260
232 /* Put some HTML in here to create a dynamic link */ 261 /* Put some HTML in here to create a dynamic link */
233 printf(_("REAL %s - %d second response time\n"), state_text(result), (int)(end_time - start_time)); 262 printf(_("REAL %s - %d second response time\n"), state_text(result),
234 } else 263 (int)(end_time - start_time));
264 } else {
235 printf("%s\n", status_line); 265 printf("%s\n", status_line);
266 }
236 267
237 /* close the connection */ 268 /* close the connection */
238 close(socket); 269 close(socket);
@@ -240,73 +271,83 @@ int main(int argc, char **argv) {
240 /* reset the alarm */ 271 /* reset the alarm */
241 alarm(0); 272 alarm(0);
242 273
243 return result; 274 exit(result);
244} 275}
245 276
246/* process command-line arguments */ 277/* process command-line arguments */
247int process_arguments(int argc, char **argv) { 278check_real_config_wrapper process_arguments(int argc, char **argv) {
248 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'}, 279 static struct option longopts[] = {
249 {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'}, 280 {"hostname", required_argument, 0, 'H'}, {"IPaddress", required_argument, 0, 'I'},
250 {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'}, 281 {"expect", required_argument, 0, 'e'}, {"url", required_argument, 0, 'u'},
251 {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'}, 282 {"port", required_argument, 0, 'p'}, {"critical", required_argument, 0, 'c'},
252 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 283 {"warning", required_argument, 0, 'w'}, {"timeout", required_argument, 0, 't'},
253 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 284 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'},
254 285 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
255 if (argc < 2) 286
256 return ERROR; 287 check_real_config_wrapper result = {
288 .errorcode = OK,
289 .config = check_real_config_init(),
290 };
291
292 if (argc < 2) {
293 result.errorcode = ERROR;
294 return result;
295 }
257 296
258 for (int i = 1; i < argc; i++) { 297 for (int i = 1; i < argc; i++) {
259 if (strcmp("-to", argv[i]) == 0) 298 if (strcmp("-to", argv[i]) == 0) {
260 strcpy(argv[i], "-t"); 299 strcpy(argv[i], "-t");
261 else if (strcmp("-wt", argv[i]) == 0) 300 } else if (strcmp("-wt", argv[i]) == 0) {
262 strcpy(argv[i], "-w"); 301 strcpy(argv[i], "-w");
263 else if (strcmp("-ct", argv[i]) == 0) 302 } else if (strcmp("-ct", argv[i]) == 0) {
264 strcpy(argv[i], "-c"); 303 strcpy(argv[i], "-c");
304 }
265 } 305 }
266 306
267 int option_char;
268 while (true) { 307 while (true) {
269 int option = 0; 308 int option = 0;
270 option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option); 309 int option_char = getopt_long(argc, argv, "+hvVI:H:e:u:p:w:c:t:", longopts, &option);
271 310
272 if (option_char == -1 || option_char == EOF) 311 if (option_char == -1 || option_char == EOF) {
273 break; 312 break;
313 }
274 314
275 switch (option_char) { 315 switch (option_char) {
276 case 'I': /* hostname */ 316 case 'I': /* hostname */
277 case 'H': /* hostname */ 317 case 'H': /* hostname */
278 if (server_address) 318 if (result.config.server_address) {
279 break; 319 break;
280 else if (is_host(optarg)) 320 } else if (is_host(optarg)) {
281 server_address = optarg; 321 result.config.server_address = optarg;
282 else 322 } else {
283 usage2(_("Invalid hostname/address"), optarg); 323 usage2(_("Invalid hostname/address"), optarg);
324 }
284 break; 325 break;
285 case 'e': /* string to expect in response header */ 326 case 'e': /* string to expect in response header */
286 server_expect = optarg; 327 result.config.server_expect = optarg;
287 break; 328 break;
288 case 'u': /* server URL */ 329 case 'u': /* server URL */
289 server_url = optarg; 330 result.config.server_url = optarg;
290 break; 331 break;
291 case 'p': /* port */ 332 case 'p': /* port */
292 if (is_intpos(optarg)) { 333 if (is_intpos(optarg)) {
293 server_port = atoi(optarg); 334 result.config.server_port = atoi(optarg);
294 } else { 335 } else {
295 usage4(_("Port must be a positive integer")); 336 usage4(_("Port must be a positive integer"));
296 } 337 }
297 break; 338 break;
298 case 'w': /* warning time threshold */ 339 case 'w': /* warning time threshold */
299 if (is_intnonneg(optarg)) { 340 if (is_intnonneg(optarg)) {
300 warning_time = atoi(optarg); 341 result.config.warning_time = atoi(optarg);
301 check_warning_time = true; 342 result.config.check_warning_time = true;
302 } else { 343 } else {
303 usage4(_("Warning time must be a positive integer")); 344 usage4(_("Warning time must be a positive integer"));
304 } 345 }
305 break; 346 break;
306 case 'c': /* critical time threshold */ 347 case 'c': /* critical time threshold */
307 if (is_intnonneg(optarg)) { 348 if (is_intnonneg(optarg)) {
308 critical_time = atoi(optarg); 349 result.config.critical_time = atoi(optarg);
309 check_critical_time = true; 350 result.config.check_critical_time = true;
310 } else { 351 } else {
311 usage4(_("Critical time must be a positive integer")); 352 usage4(_("Critical time must be a positive integer"));
312 } 353 }
@@ -332,25 +373,28 @@ int process_arguments(int argc, char **argv) {
332 } 373 }
333 } 374 }
334 375
335 option_char = optind; 376 int option_char = optind;
336 if (server_address == NULL && argc > option_char) { 377 if (result.config.server_address == NULL && argc > option_char) {
337 if (is_host(argv[option_char])) { 378 if (is_host(argv[option_char])) {
338 server_address = argv[option_char++]; 379 result.config.server_address = argv[option_char++];
339 } else { 380 } else {
340 usage2(_("Invalid hostname/address"), argv[option_char]); 381 usage2(_("Invalid hostname/address"), argv[option_char]);
341 } 382 }
342 } 383 }
343 384
344 if (server_address == NULL) 385 if (result.config.server_address == NULL) {
345 usage4(_("You must provide a server to check")); 386 usage4(_("You must provide a server to check"));
387 }
346 388
347 if (host_name == NULL) 389 if (result.config.host_name == NULL) {
348 host_name = strdup(server_address); 390 result.config.host_name = strdup(result.config.server_address);
391 }
349 392
350 if (server_expect == NULL) 393 if (result.config.server_expect == NULL) {
351 server_expect = strdup(EXPECT); 394 result.config.server_expect = strdup(EXPECT);
395 }
352 396
353 return OK; 397 return result;
354} 398}
355 399
356void print_help(void) { 400void print_help(void) {
@@ -388,7 +432,8 @@ void print_help(void) {
388 printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host.")); 432 printf("%s\n", _("This plugin will attempt to open an RTSP connection with the host."));
389 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); 433 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
390 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,")); 434 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful connects,"));
391 printf("%s\n", _("but incorrect response messages from the host result in STATE_WARNING return")); 435 printf("%s\n",
436 _("but incorrect response messages from the host result in STATE_WARNING return"));
392 printf("%s\n", _("values.")); 437 printf("%s\n", _("values."));
393 438
394 printf(UT_SUPPORT); 439 printf(UT_SUPPORT);
diff --git a/plugins/check_real.d/config.h b/plugins/check_real.d/config.h
new file mode 100644
index 00000000..c4663cf9
--- /dev/null
+++ b/plugins/check_real.d/config.h
@@ -0,0 +1,37 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 PORT = 554
8};
9
10typedef struct {
11 char *server_address;
12 char *host_name;
13 int server_port;
14 char *server_url;
15
16 char *server_expect;
17 int warning_time;
18 bool check_warning_time;
19 int critical_time;
20 bool check_critical_time;
21} check_real_config;
22
23check_real_config check_real_config_init() {
24 check_real_config tmp = {
25 .server_address = NULL,
26 .host_name = NULL,
27 .server_port = PORT,
28 .server_url = NULL,
29
30 .server_expect = NULL,
31 .warning_time = 0,
32 .check_warning_time = false,
33 .critical_time = 0,
34 .check_critical_time = false,
35 };
36 return tmp;
37}
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index e6369e63..83ad575c 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -1,32 +1,32 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_smtp plugin 3 * Monitoring check_smtp plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_smtp plugin 10 * This file contains the check_smtp plugin
11* 11 *
12* This plugin will attempt to open an SMTP connection with the host. 12 * This plugin will attempt to open an SMTP connection with the host.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_smtp"; 31const char *progname = "check_smtp";
32const char *copyright = "2000-2024"; 32const char *copyright = "2000-2024";
@@ -36,400 +36,415 @@ const char *email = "devel@monitoring-plugins.org";
36#include "netutils.h" 36#include "netutils.h"
37#include "utils.h" 37#include "utils.h"
38#include "base64.h" 38#include "base64.h"
39#include "regex.h"
39 40
40#include <ctype.h> 41#include <ctype.h>
42#include "check_smtp.d/config.h"
43#include "../lib/states.h"
44
45#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
46#define SMTP_HELO "HELO "
47#define SMTP_EHLO "EHLO "
48#define SMTP_LHLO "LHLO "
49#define SMTP_QUIT "QUIT\r\n"
50#define SMTP_STARTTLS "STARTTLS\r\n"
51#define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n"
41 52
53#define EHLO_SUPPORTS_STARTTLS 1
54
55typedef struct {
56 int errorcode;
57 check_smtp_config config;
58} check_smtp_config_wrapper;
59static check_smtp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
60
61int my_recv(check_smtp_config config, void *buf, int num, int socket_descriptor,
62 bool ssl_established) {
42#ifdef HAVE_SSL 63#ifdef HAVE_SSL
43static bool check_cert = false; 64 if ((config.use_starttls || config.use_ssl) && ssl_established) {
44static int days_till_exp_warn, days_till_exp_crit; 65 return np_net_ssl_read(buf, num);
45# define my_recv(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) 66 }
46# define my_send(buf, len) (((use_starttls || use_ssl) && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) 67 return (int)read(socket_descriptor, buf, (size_t)num);
47#else /* ifndef HAVE_SSL */ 68#else /* ifndef HAVE_SSL */
48# define my_recv(buf, len) read(sd, buf, len) 69 return read(socket_descriptor, buf, len)
49# define my_send(buf, len) send(sd, buf, len, 0)
50#endif 70#endif
71}
51 72
52enum { 73int my_send(check_smtp_config config, void *buf, int num, int socket_descriptor,
53 SMTP_PORT = 25, 74 bool ssl_established) {
54 SMTPS_PORT = 465 75#ifdef HAVE_SSL
55}; 76 if ((config.use_starttls || config.use_ssl) && ssl_established) {
56#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
57#define SMTP_EXPECT "220"
58#define SMTP_HELO "HELO "
59#define SMTP_EHLO "EHLO "
60#define SMTP_LHLO "LHLO "
61#define SMTP_QUIT "QUIT\r\n"
62#define SMTP_STARTTLS "STARTTLS\r\n"
63#define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n"
64 77
65#define EHLO_SUPPORTS_STARTTLS 1 78 return np_net_ssl_write(buf, num);
79 }
80 return (int)send(socket_descriptor, buf, (size_t)num, 0);
81#else /* ifndef HAVE_SSL */
82 return send(socket_descriptor, buf, len, 0);
83#endif
84}
66 85
67static int process_arguments (int, char **); 86static void print_help(void);
68static int validate_arguments (void); 87void print_usage(void);
69static void print_help (void); 88static char *smtp_quit(check_smtp_config /*config*/, char /*buffer*/[MAX_INPUT_BUFFER],
70void print_usage (void); 89 int /*socket_descriptor*/, bool /*ssl_established*/);
71static void smtp_quit(void); 90static int recvline(char * /*buf*/, size_t /*bufsize*/, check_smtp_config /*config*/,
72static int recvline(char *, size_t); 91 int /*socket_descriptor*/, bool /*ssl_established*/);
73static int recvlines(char *, size_t); 92static int recvlines(check_smtp_config /*config*/, char * /*buf*/, size_t /*bufsize*/,
74static int my_close(void); 93 int /*socket_descriptor*/, bool /*ssl_established*/);
94static int my_close(int /*socket_descriptor*/);
75 95
76#include "regex.h"
77static regex_t preg;
78static regmatch_t pmatch[10];
79static char errbuf[MAX_INPUT_BUFFER];
80static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
81static int eflags = 0;
82static int errcode, excode;
83
84static int server_port = SMTP_PORT;
85static int server_port_option = 0;
86static char *server_address = NULL;
87static char *server_expect = NULL;
88static char *mail_command = NULL;
89static char *from_arg = NULL;
90static int send_mail_from=0;
91static int ncommands=0;
92static int command_size=0;
93static int nresponses=0;
94static int response_size=0;
95static char **commands = NULL;
96static char **responses = NULL;
97static char *authtype = NULL;
98static char *authuser = NULL;
99static char *authpass = NULL;
100static double warning_time = 0;
101static bool check_warning_time = false;
102static double critical_time = 0;
103static bool check_critical_time = false;
104static int verbose = 0; 96static int verbose = 0;
105static bool use_ssl = false;
106static bool use_starttls = false;
107static bool use_sni = false;
108static bool use_proxy_prefix = false;
109static bool use_ehlo = false;
110static bool use_lhlo = false;
111static bool ssl_established = false;
112static char *localhostname = NULL;
113static int sd;
114static char buffer[MAX_INPUT_BUFFER];
115enum {
116 TCP_PROTOCOL = 1,
117 UDP_PROTOCOL = 2,
118};
119static bool ignore_send_quit_failure = false;
120
121
122int
123main (int argc, char **argv)
124{
125 bool supports_tls = false;
126 int n = 0;
127 double elapsed_time;
128 long microsec;
129 int result = STATE_UNKNOWN;
130 char *cmd_str = NULL;
131 char *helocmd = NULL;
132 char *error_msg = "";
133 char *server_response = NULL;
134 struct timeval tv;
135 97
136 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */ 98int main(int argc, char **argv) {
137 (void) signal (SIGPIPE, SIG_IGN); 99 setlocale(LC_ALL, "");
138 100 bindtextdomain(PACKAGE, LOCALEDIR);
139 setlocale (LC_ALL, ""); 101 textdomain(PACKAGE);
140 bindtextdomain (PACKAGE, LOCALEDIR);
141 textdomain (PACKAGE);
142 102
143 /* Parse extra opts if any */ 103 /* Parse extra opts if any */
144 argv=np_extra_opts (&argc, argv, progname); 104 argv = np_extra_opts(&argc, argv, progname);
145 105
146 if (process_arguments (argc, argv) == ERROR) 106 check_smtp_config_wrapper tmp_config = process_arguments(argc, argv);
147 usage4 (_("Could not parse arguments")); 107
108 if (tmp_config.errorcode == ERROR) {
109 usage4(_("Could not parse arguments"));
110 }
111
112 const check_smtp_config config = tmp_config.config;
148 113
149 /* If localhostname not set on command line, use gethostname to set */ 114 /* If localhostname not set on command line, use gethostname to set */
150 if(! localhostname){ 115 char *localhostname = config.localhostname;
151 localhostname = malloc (HOST_MAX_BYTES); 116 if (!localhostname) {
152 if(!localhostname){ 117 localhostname = malloc(HOST_MAX_BYTES);
118 if (!localhostname) {
153 printf(_("malloc() failed!\n")); 119 printf(_("malloc() failed!\n"));
154 return STATE_CRITICAL; 120 exit(STATE_CRITICAL);
155 } 121 }
156 if(gethostname(localhostname, HOST_MAX_BYTES)){ 122 if (gethostname(localhostname, HOST_MAX_BYTES)) {
157 printf(_("gethostname() failed!\n")); 123 printf(_("gethostname() failed!\n"));
158 return STATE_CRITICAL; 124 exit(STATE_CRITICAL);
159 } 125 }
160 } 126 }
161 if(use_lhlo) 127
162 xasprintf (&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n"); 128 char *helocmd = NULL;
163 else if(use_ehlo) 129 if (config.use_lhlo) {
164 xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n"); 130 xasprintf(&helocmd, "%s%s%s", SMTP_LHLO, localhostname, "\r\n");
165 else 131 } else if (config.use_ehlo) {
166 xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n"); 132 xasprintf(&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
167 133 } else {
168 if (verbose) 134 xasprintf(&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");
135 }
136
137 if (verbose) {
169 printf("HELOCMD: %s", helocmd); 138 printf("HELOCMD: %s", helocmd);
139 }
170 140
141 char *mail_command = strdup("MAIL ");
142 char *cmd_str = NULL;
171 /* initialize the MAIL command with optional FROM command */ 143 /* initialize the MAIL command with optional FROM command */
172 xasprintf (&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n"); 144 xasprintf(&cmd_str, "%sFROM:<%s>%s", mail_command, config.from_arg, "\r\n");
173 145
174 if (verbose && send_mail_from) 146 if (verbose && config.send_mail_from) {
175 printf ("FROM CMD: %s", cmd_str); 147 printf("FROM CMD: %s", cmd_str);
148 }
149
150 /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */
151 (void)signal(SIGPIPE, SIG_IGN);
176 152
177 /* initialize alarm signal handling */ 153 /* initialize alarm signal handling */
178 (void) signal (SIGALRM, socket_timeout_alarm_handler); 154 (void)signal(SIGALRM, socket_timeout_alarm_handler);
179 155
180 /* set socket timeout */ 156 /* set socket timeout */
181 (void) alarm (socket_timeout); 157 (void)alarm(socket_timeout);
182 158
159 struct timeval start_time;
183 /* start timer */ 160 /* start timer */
184 gettimeofday (&tv, NULL); 161 gettimeofday(&start_time, NULL);
185 162
163 int socket_descriptor = 0;
186 /* try to connect to the host at the given port number */ 164 /* try to connect to the host at the given port number */
187 result = my_tcp_connect (server_address, server_port, &sd); 165 mp_state_enum result =
166 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
188 167
168 char *error_msg = "";
169 char buffer[MAX_INPUT_BUFFER];
170 bool ssl_established = false;
189 if (result == STATE_OK) { /* we connected */ 171 if (result == STATE_OK) { /* we connected */
190 /* If requested, send PROXY header */ 172 /* If requested, send PROXY header */
191 if (use_proxy_prefix) { 173 if (config.use_proxy_prefix) {
192 if (verbose) 174 if (verbose) {
193 printf ("Sending header %s\n", PROXY_PREFIX); 175 printf("Sending header %s\n", PROXY_PREFIX);
194 my_send(PROXY_PREFIX, strlen(PROXY_PREFIX)); 176 }
177 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
195 } 178 }
196 179
197#ifdef HAVE_SSL 180#ifdef HAVE_SSL
198 if (use_ssl) { 181 if (config.use_ssl) {
199 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); 182 result = np_net_ssl_init_with_hostname(socket_descriptor,
183 (config.use_sni ? config.server_address : NULL));
200 if (result != STATE_OK) { 184 if (result != STATE_OK) {
201 printf (_("CRITICAL - Cannot create SSL context.\n")); 185 printf(_("CRITICAL - Cannot create SSL context.\n"));
202 close(sd); 186 close(socket_descriptor);
203 np_net_ssl_cleanup(); 187 np_net_ssl_cleanup();
204 return STATE_CRITICAL; 188 exit(STATE_CRITICAL);
205 } else {
206 ssl_established = 1;
207 } 189 }
190 ssl_established = true;
208 } 191 }
209#endif 192#endif
210 193
211 /* watch for the SMTP connection string and */ 194 /* watch for the SMTP connection string and */
212 /* return a WARNING status if we couldn't read any data */ 195 /* return a WARNING status if we couldn't read any data */
213 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 196 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
214 printf (_("recv() failed\n")); 197 printf(_("recv() failed\n"));
215 return STATE_WARNING; 198 exit(STATE_WARNING);
216 } 199 }
217 200
201 char *server_response = NULL;
218 /* save connect return (220 hostname ..) for later use */ 202 /* save connect return (220 hostname ..) for later use */
219 xasprintf(&server_response, "%s", buffer); 203 xasprintf(&server_response, "%s", buffer);
220 204
221 /* send the HELO/EHLO command */ 205 /* send the HELO/EHLO command */
222 my_send(helocmd, strlen(helocmd)); 206 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
223 207
224 /* allow for response to helo command to reach us */ 208 /* allow for response to helo command to reach us */
225 if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) { 209 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
226 printf (_("recv() failed\n")); 210 printf(_("recv() failed\n"));
227 return STATE_WARNING; 211 exit(STATE_WARNING);
228 } else if(use_ehlo || use_lhlo){ 212 }
229 if(strstr(buffer, "250 STARTTLS") != NULL || 213
230 strstr(buffer, "250-STARTTLS") != NULL){ 214 bool supports_tls = false;
231 supports_tls=true; 215 if (config.use_ehlo || config.use_lhlo) {
216 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
217 supports_tls = true;
232 } 218 }
233 } 219 }
234 220
235 if(use_starttls && ! supports_tls){ 221 if (config.use_starttls && !supports_tls) {
236 printf(_("WARNING - TLS not supported by server\n")); 222 printf(_("WARNING - TLS not supported by server\n"));
237 smtp_quit(); 223 smtp_quit(config, buffer, socket_descriptor, ssl_established);
238 return STATE_WARNING; 224 exit(STATE_WARNING);
239 } 225 }
240 226
241#ifdef HAVE_SSL 227#ifdef HAVE_SSL
242 if(use_starttls) { 228 if (config.use_starttls) {
243 /* send the STARTTLS command */ 229 /* send the STARTTLS command */
244 send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); 230 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
245 231
246 recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */ 232 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
247 if (!strstr (buffer, SMTP_EXPECT)) { 233 ssl_established); /* wait for it */
248 printf (_("Server does not support STARTTLS\n")); 234 if (!strstr(buffer, SMTP_EXPECT)) {
249 smtp_quit(); 235 printf(_("Server does not support STARTTLS\n"));
250 return STATE_UNKNOWN; 236 smtp_quit(config, buffer, socket_descriptor, ssl_established);
251 } 237 exit(STATE_UNKNOWN);
252 result = np_net_ssl_init_with_hostname(sd, (use_sni ? server_address : NULL)); 238 }
253 if(result != STATE_OK) { 239
254 printf (_("CRITICAL - Cannot create SSL context.\n")); 240 result = np_net_ssl_init_with_hostname(socket_descriptor,
255 close(sd); 241 (config.use_sni ? config.server_address : NULL));
256 np_net_ssl_cleanup(); 242 if (result != STATE_OK) {
257 return STATE_CRITICAL; 243 printf(_("CRITICAL - Cannot create SSL context.\n"));
258 } else { 244 close(socket_descriptor);
259 ssl_established = 1; 245 np_net_ssl_cleanup();
260 } 246 exit(STATE_CRITICAL);
261 247 }
262 /* 248
263 * Resend the EHLO command. 249 ssl_established = true;
264 * 250
265 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge 251 /*
266 * obtained from the server, such as the list of SMTP service 252 * Resend the EHLO command.
267 * extensions, which was not obtained from the TLS negotiation 253 *
268 * itself. The client SHOULD send an EHLO command as the first 254 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
269 * command after a successful TLS negotiation.'' For this 255 * obtained from the server, such as the list of SMTP service
270 * reason, some MTAs will not allow an AUTH LOGIN command before 256 * extensions, which was not obtained from the TLS negotiation
271 * we resent EHLO via TLS. 257 * itself. The client SHOULD send an EHLO command as the first
272 */ 258 * command after a successful TLS negotiation.'' For this
273 if (my_send(helocmd, strlen(helocmd)) <= 0) { 259 * reason, some MTAs will not allow an AUTH LOGIN command before
274 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS.")); 260 * we resent EHLO via TLS.
275 my_close(); 261 */
276 return STATE_UNKNOWN; 262 if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
277 } 263 0) {
278 if (verbose) 264 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
279 printf(_("sent %s"), helocmd); 265 my_close(socket_descriptor);
280 if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 266 exit(STATE_UNKNOWN);
281 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); 267 }
282 my_close(); 268
283 return STATE_UNKNOWN; 269 if (verbose) {
284 } 270 printf(_("sent %s"), helocmd);
285 if (verbose) { 271 }
286 printf("%s", buffer);
287 }
288 272
289# ifdef USE_OPENSSL 273 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <=
290 if ( check_cert ) { 274 0) {
291 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 275 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
292 smtp_quit(); 276 my_close(socket_descriptor);
293 my_close(); 277 exit(STATE_UNKNOWN);
294 return result; 278 }
295 } 279
296# endif /* USE_OPENSSL */ 280 if (verbose) {
281 printf("%s", buffer);
282 }
283
284# ifdef USE_OPENSSL
285 if (config.check_cert) {
286 result =
287 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
288 smtp_quit(config, buffer, socket_descriptor, ssl_established);
289 my_close(socket_descriptor);
290 exit(result);
291 }
292# endif /* USE_OPENSSL */
297 } 293 }
298#endif 294#endif
299 295
300 if (verbose) 296 if (verbose) {
301 printf ("%s", buffer); 297 printf("%s", buffer);
298 }
302 299
303 /* save buffer for later use */ 300 /* save buffer for later use */
304 xasprintf(&server_response, "%s%s", server_response, buffer); 301 xasprintf(&server_response, "%s%s", server_response, buffer);
305 /* strip the buffer of carriage returns */ 302 /* strip the buffer of carriage returns */
306 strip (server_response); 303 strip(server_response);
307 304
308 /* make sure we find the droids we are looking for */ 305 /* make sure we find the droids we are looking for */
309 if (!strstr (server_response, server_expect)) { 306 if (!strstr(server_response, config.server_expect)) {
310 if (server_port == SMTP_PORT) 307 if (config.server_port == SMTP_PORT) {
311 printf (_("Invalid SMTP response received from host: %s\n"), server_response); 308 printf(_("Invalid SMTP response received from host: %s\n"), server_response);
312 else 309 } else {
313 printf (_("Invalid SMTP response received from host on port %d: %s\n"), 310 printf(_("Invalid SMTP response received from host on port %d: %s\n"),
314 server_port, server_response); 311 config.server_port, server_response);
315 return STATE_WARNING; 312 }
313 exit(STATE_WARNING);
316 } 314 }
317 315
318 if (send_mail_from) { 316 if (config.send_mail_from) {
319 my_send(cmd_str, strlen(cmd_str)); 317 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
320 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 318 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
321 printf("%s", buffer); 319 1 &&
320 verbose) {
321 printf("%s", buffer);
322 }
322 } 323 }
323 324
324 n = 0; 325 int counter = 0;
325 while (n < ncommands) { 326 while (counter < config.ncommands) {
326 xasprintf (&cmd_str, "%s%s", commands[n], "\r\n"); 327 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
327 my_send(cmd_str, strlen(cmd_str)); 328 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
328 if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose) 329 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
330 1 &&
331 verbose) {
329 printf("%s", buffer); 332 printf("%s", buffer);
330 strip (buffer); 333 }
331 if (n < nresponses) { 334 strip(buffer);
332 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 335 if (counter < config.nresponses) {
333 errcode = regcomp (&preg, responses[n], cflags); 336 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
337 regex_t preg;
338 int errcode = regcomp(&preg, config.responses[counter], cflags);
339 char errbuf[MAX_INPUT_BUFFER];
334 if (errcode != 0) { 340 if (errcode != 0) {
335 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 341 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
336 printf (_("Could Not Compile Regular Expression")); 342 printf(_("Could Not Compile Regular Expression"));
337 return ERROR; 343 exit(STATE_UNKNOWN);
338 } 344 }
339 excode = regexec (&preg, buffer, 10, pmatch, eflags); 345
346 regmatch_t pmatch[10];
347 int eflags = 0;
348 int excode = regexec(&preg, buffer, 10, pmatch, eflags);
340 if (excode == 0) { 349 if (excode == 0) {
341 result = STATE_OK; 350 result = STATE_OK;
342 } 351 } else if (excode == REG_NOMATCH) {
343 else if (excode == REG_NOMATCH) {
344 result = STATE_WARNING; 352 result = STATE_WARNING;
345 printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]); 353 printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"),
346 } 354 state_text(result), buffer, config.commands[counter]);
347 else { 355 } else {
348 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER); 356 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
349 printf (_("Execute Error: %s\n"), errbuf); 357 printf(_("Execute Error: %s\n"), errbuf);
350 result = STATE_UNKNOWN; 358 result = STATE_UNKNOWN;
351 } 359 }
352 } 360 }
353 n++; 361 counter++;
354 } 362 }
355 363
356 if (authtype != NULL) { 364 if (config.authtype != NULL) {
357 if (strcmp (authtype, "LOGIN") == 0) { 365 if (strcmp(config.authtype, "LOGIN") == 0) {
358 char *abuf; 366 char *abuf;
359 int ret; 367 int ret;
360 do { 368 do {
361 if (authuser == NULL) { 369 if (config.authuser == NULL) {
362 result = STATE_CRITICAL; 370 result = STATE_CRITICAL;
363 xasprintf(&error_msg, _("no authuser specified, ")); 371 xasprintf(&error_msg, _("no authuser specified, "));
364 break; 372 break;
365 } 373 }
366 if (authpass == NULL) { 374 if (config.authpass == NULL) {
367 result = STATE_CRITICAL; 375 result = STATE_CRITICAL;
368 xasprintf(&error_msg, _("no authpass specified, ")); 376 xasprintf(&error_msg, _("no authpass specified, "));
369 break; 377 break;
370 } 378 }
371 379
372 /* send AUTH LOGIN */ 380 /* send AUTH LOGIN */
373 my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN)); 381 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
374 if (verbose) 382 ssl_established);
375 printf (_("sent %s\n"), "AUTH LOGIN"); 383 if (verbose) {
384 printf(_("sent %s\n"), "AUTH LOGIN");
385 }
376 386
377 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 387 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
388 ssl_established)) <= 0) {
378 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, ")); 389 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
379 result = STATE_WARNING; 390 result = STATE_WARNING;
380 break; 391 break;
381 } 392 }
382 if (verbose) 393 if (verbose) {
383 printf (_("received %s\n"), buffer); 394 printf(_("received %s\n"), buffer);
395 }
384 396
385 if (strncmp (buffer, "334", 3) != 0) { 397 if (strncmp(buffer, "334", 3) != 0) {
386 result = STATE_CRITICAL; 398 result = STATE_CRITICAL;
387 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, ")); 399 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
388 break; 400 break;
389 } 401 }
390 402
391 /* encode authuser with base64 */ 403 /* encode authuser with base64 */
392 base64_encode_alloc (authuser, strlen(authuser), &abuf); 404 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
393 xasprintf(&abuf, "%s\r\n", abuf); 405 xasprintf(&abuf, "%s\r\n", abuf);
394 my_send(abuf, strlen(abuf)); 406 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
395 if (verbose) 407 if (verbose) {
396 printf (_("sent %s\n"), abuf); 408 printf(_("sent %s\n"), abuf);
409 }
397 410
398 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 411 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
412 ssl_established)) <= 0) {
399 result = STATE_CRITICAL; 413 result = STATE_CRITICAL;
400 xasprintf(&error_msg, _("recv() failed after sending authuser, ")); 414 xasprintf(&error_msg, _("recv() failed after sending authuser, "));
401 break; 415 break;
402 } 416 }
403 if (verbose) { 417 if (verbose) {
404 printf (_("received %s\n"), buffer); 418 printf(_("received %s\n"), buffer);
405 } 419 }
406 if (strncmp (buffer, "334", 3) != 0) { 420 if (strncmp(buffer, "334", 3) != 0) {
407 result = STATE_CRITICAL; 421 result = STATE_CRITICAL;
408 xasprintf(&error_msg, _("invalid response received after authuser, ")); 422 xasprintf(&error_msg, _("invalid response received after authuser, "));
409 break; 423 break;
410 } 424 }
411 /* encode authpass with base64 */ 425 /* encode authpass with base64 */
412 base64_encode_alloc (authpass, strlen(authpass), &abuf); 426 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
413 xasprintf(&abuf, "%s\r\n", abuf); 427 xasprintf(&abuf, "%s\r\n", abuf);
414 my_send(abuf, strlen(abuf)); 428 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
415 if (verbose) { 429 if (verbose) {
416 printf (_("sent %s\n"), abuf); 430 printf(_("sent %s\n"), abuf);
417 } 431 }
418 if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) { 432 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
433 ssl_established)) <= 0) {
419 result = STATE_CRITICAL; 434 result = STATE_CRITICAL;
420 xasprintf(&error_msg, _("recv() failed after sending authpass, ")); 435 xasprintf(&error_msg, _("recv() failed after sending authpass, "));
421 break; 436 break;
422 } 437 }
423 if (verbose) { 438 if (verbose) {
424 printf (_("received %s\n"), buffer); 439 printf(_("received %s\n"), buffer);
425 } 440 }
426 if (strncmp (buffer, "235", 3) != 0) { 441 if (strncmp(buffer, "235", 3) != 0) {
427 result = STATE_CRITICAL; 442 result = STATE_CRITICAL;
428 xasprintf(&error_msg, _("invalid response received after authpass, ")); 443 xasprintf(&error_msg, _("invalid response received after authpass, "));
429 break; 444 break;
430 } 445 }
431 break; 446 break;
432 } while (0); 447 } while (false);
433 } else { 448 } else {
434 result = STATE_CRITICAL; 449 result = STATE_CRITICAL;
435 xasprintf(&error_msg, _("only authtype LOGIN is supported, ")); 450 xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
@@ -437,243 +452,249 @@ main (int argc, char **argv)
437 } 452 }
438 453
439 /* tell the server we're done */ 454 /* tell the server we're done */
440 smtp_quit(); 455 smtp_quit(config, buffer, socket_descriptor, ssl_established);
441 456
442 /* finally close the connection */ 457 /* finally close the connection */
443 close (sd); 458 close(socket_descriptor);
444 } 459 }
445 460
446 /* reset the alarm */ 461 /* reset the alarm */
447 alarm (0); 462 alarm(0);
448 463
449 microsec = deltime (tv); 464 long microsec = deltime(start_time);
450 elapsed_time = (double)microsec / 1.0e6; 465 double elapsed_time = (double)microsec / 1.0e6;
451 466
452 if (result == STATE_OK) { 467 if (result == STATE_OK) {
453 if (check_critical_time && elapsed_time > critical_time) 468 if (config.check_critical_time && elapsed_time > config.critical_time) {
454 result = STATE_CRITICAL; 469 result = STATE_CRITICAL;
455 else if (check_warning_time && elapsed_time > warning_time) 470 } else if (config.check_warning_time && elapsed_time > config.warning_time) {
456 result = STATE_WARNING; 471 result = STATE_WARNING;
472 }
457 } 473 }
458 474
459 printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), 475 printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg,
460 state_text (result), 476 elapsed_time, verbose ? ", " : "", verbose ? buffer : "",
461 error_msg, 477 fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time,
462 elapsed_time, 478 config.check_critical_time, config.critical_time, true, 0, false, 0));
463 verbose?", ":"", verbose?buffer:"",
464 fperfdata ("time", elapsed_time, "s",
465 (int)check_warning_time, warning_time,
466 (int)check_critical_time, critical_time,
467 true, 0, false, 0));
468 479
469 return result; 480 exit(result);
470} 481}
471 482
472
473
474/* process command-line arguments */ 483/* process command-line arguments */
475int 484check_smtp_config_wrapper process_arguments(int argc, char **argv) {
476process_arguments (int argc, char **argv)
477{
478 int c;
479 char* temp;
480
481 bool implicit_tls = false;
482
483 enum { 485 enum {
484 SNI_OPTION 486 SNI_OPTION = CHAR_MAX + 1
485 }; 487 };
486 488
487 int option = 0; 489 int option = 0;
488 static struct option longopts[] = { 490 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
489 {"hostname", required_argument, 0, 'H'}, 491 {"expect", required_argument, 0, 'e'},
490 {"expect", required_argument, 0, 'e'}, 492 {"critical", required_argument, 0, 'c'},
491 {"critical", required_argument, 0, 'c'}, 493 {"warning", required_argument, 0, 'w'},
492 {"warning", required_argument, 0, 'w'}, 494 {"timeout", required_argument, 0, 't'},
493 {"timeout", required_argument, 0, 't'}, 495 {"port", required_argument, 0, 'p'},
494 {"port", required_argument, 0, 'p'}, 496 {"from", required_argument, 0, 'f'},
495 {"from", required_argument, 0, 'f'}, 497 {"fqdn", required_argument, 0, 'F'},
496 {"fqdn", required_argument, 0, 'F'}, 498 {"authtype", required_argument, 0, 'A'},
497 {"authtype", required_argument, 0, 'A'}, 499 {"authuser", required_argument, 0, 'U'},
498 {"authuser", required_argument, 0, 'U'}, 500 {"authpass", required_argument, 0, 'P'},
499 {"authpass", required_argument, 0, 'P'}, 501 {"command", required_argument, 0, 'C'},
500 {"command", required_argument, 0, 'C'}, 502 {"response", required_argument, 0, 'R'},
501 {"response", required_argument, 0, 'R'}, 503 {"verbose", no_argument, 0, 'v'},
502 {"verbose", no_argument, 0, 'v'}, 504 {"version", no_argument, 0, 'V'},
503 {"version", no_argument, 0, 'V'}, 505 {"use-ipv4", no_argument, 0, '4'},
504 {"use-ipv4", no_argument, 0, '4'}, 506 {"use-ipv6", no_argument, 0, '6'},
505 {"use-ipv6", no_argument, 0, '6'}, 507 {"help", no_argument, 0, 'h'},
506 {"help", no_argument, 0, 'h'}, 508 {"lmtp", no_argument, 0, 'L'},
507 {"lmtp", no_argument, 0, 'L'}, 509 {"ssl", no_argument, 0, 's'},
508 {"ssl", no_argument, 0, 's'}, 510 {"tls", no_argument, 0, 's'},
509 {"tls", no_argument, 0, 's'}, 511 {"starttls", no_argument, 0, 'S'},
510 {"starttls",no_argument,0,'S'}, 512 {"sni", no_argument, 0, SNI_OPTION},
511 {"sni", no_argument, 0, SNI_OPTION}, 513 {"certificate", required_argument, 0, 'D'},
512 {"certificate",required_argument,0,'D'}, 514 {"ignore-quit-failure", no_argument, 0, 'q'},
513 {"ignore-quit-failure",no_argument,0,'q'}, 515 {"proxy", no_argument, 0, 'r'},
514 {"proxy",no_argument,0,'r'}, 516 {0, 0, 0, 0}};
515 {0, 0, 0, 0} 517
518 check_smtp_config_wrapper result = {
519 .config = check_smtp_config_init(),
520 .errorcode = OK,
516 }; 521 };
517 522
518 if (argc < 2) 523 if (argc < 2) {
519 return ERROR; 524 result.errorcode = ERROR;
525 return result;
526 }
520 527
521 for (c = 1; c < argc; c++) { 528 for (int index = 1; index < argc; index++) {
522 if (strcmp ("-to", argv[c]) == 0) 529 if (strcmp("-to", argv[index]) == 0) {
523 strcpy (argv[c], "-t"); 530 strcpy(argv[index], "-t");
524 else if (strcmp ("-wt", argv[c]) == 0) 531 } else if (strcmp("-wt", argv[index]) == 0) {
525 strcpy (argv[c], "-w"); 532 strcpy(argv[index], "-w");
526 else if (strcmp ("-ct", argv[c]) == 0) 533 } else if (strcmp("-ct", argv[index]) == 0) {
527 strcpy (argv[c], "-c"); 534 strcpy(argv[index], "-c");
535 }
528 } 536 }
529 537
530 while (1) { 538 int command_size = 0;
531 c = getopt_long (argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", 539 int response_size = 0;
532 longopts, &option); 540 bool implicit_tls = false;
541 int server_port_option = 0;
542 while (true) {
543 int opt_index =
544 getopt_long(argc, argv, "+hVv46Lrt:p:f:e:c:w:H:C:R:sSD:F:A:U:P:q", longopts, &option);
533 545
534 if (c == -1 || c == EOF) 546 if (opt_index == -1 || opt_index == EOF) {
535 break; 547 break;
548 }
536 549
537 switch (c) { 550 switch (opt_index) {
538 case 'H': /* hostname */ 551 case 'H': /* hostname */
539 if (is_host (optarg)) { 552 if (is_host(optarg)) {
540 server_address = optarg; 553 result.config.server_address = optarg;
541 } 554 } else {
542 else { 555 usage2(_("Invalid hostname/address"), optarg);
543 usage2 (_("Invalid hostname/address"), optarg);
544 } 556 }
545 break; 557 break;
546 case 'p': /* port */ 558 case 'p': /* port */
547 if (is_intpos (optarg)) 559 if (is_intpos(optarg)) {
548 server_port_option = atoi (optarg); 560 server_port_option = atoi(optarg);
549 else 561 } else {
550 usage4 (_("Port must be a positive integer")); 562 usage4(_("Port must be a positive integer"));
563 }
551 break; 564 break;
552 case 'F': 565 case 'F':
553 /* localhostname */ 566 /* localhostname */
554 localhostname = strdup(optarg); 567 result.config.localhostname = strdup(optarg);
555 break; 568 break;
556 case 'f': /* from argument */ 569 case 'f': /* from argument */
557 from_arg = optarg + strspn(optarg, "<"); 570 result.config.from_arg = optarg + strspn(optarg, "<");
558 from_arg = strndup(from_arg, strcspn(from_arg, ">")); 571 result.config.from_arg =
559 send_mail_from = 1; 572 strndup(result.config.from_arg, strcspn(result.config.from_arg, ">"));
573 result.config.send_mail_from = true;
560 break; 574 break;
561 case 'A': 575 case 'A':
562 authtype = optarg; 576 result.config.authtype = optarg;
563 use_ehlo = true; 577 result.config.use_ehlo = true;
564 break; 578 break;
565 case 'U': 579 case 'U':
566 authuser = optarg; 580 result.config.authuser = optarg;
567 break; 581 break;
568 case 'P': 582 case 'P':
569 authpass = optarg; 583 result.config.authpass = optarg;
570 break; 584 break;
571 case 'e': /* server expect string on 220 */ 585 case 'e': /* server expect string on 220 */
572 server_expect = optarg; 586 result.config.server_expect = optarg;
573 break; 587 break;
574 case 'C': /* commands */ 588 case 'C': /* commands */
575 if (ncommands >= command_size) { 589 if (result.config.ncommands >= command_size) {
576 command_size+=8; 590 command_size += 8;
577 commands = realloc (commands, sizeof(char *) * command_size); 591 result.config.commands =
578 if (commands == NULL) 592 realloc(result.config.commands, sizeof(char *) * command_size);
579 die (STATE_UNKNOWN, 593 if (result.config.commands == NULL) {
580 _("Could not realloc() units [%d]\n"), ncommands); 594 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
595 result.config.ncommands);
596 }
581 } 597 }
582 commands[ncommands] = (char *) malloc (sizeof(char) * 255); 598 result.config.commands[result.config.ncommands] = (char *)malloc(sizeof(char) * 255);
583 strncpy (commands[ncommands], optarg, 255); 599 strncpy(result.config.commands[result.config.ncommands], optarg, 255);
584 ncommands++; 600 result.config.ncommands++;
585 break; 601 break;
586 case 'R': /* server responses */ 602 case 'R': /* server responses */
587 if (nresponses >= response_size) { 603 if (result.config.nresponses >= response_size) {
588 response_size += 8; 604 response_size += 8;
589 responses = realloc (responses, sizeof(char *) * response_size); 605 result.config.responses =
590 if (responses == NULL) 606 realloc(result.config.responses, sizeof(char *) * response_size);
591 die (STATE_UNKNOWN, 607 if (result.config.responses == NULL) {
592 _("Could not realloc() units [%d]\n"), nresponses); 608 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
609 result.config.nresponses);
610 }
593 } 611 }
594 responses[nresponses] = (char *) malloc (sizeof(char) * 255); 612 result.config.responses[result.config.nresponses] = (char *)malloc(sizeof(char) * 255);
595 strncpy (responses[nresponses], optarg, 255); 613 strncpy(result.config.responses[result.config.nresponses], optarg, 255);
596 nresponses++; 614 result.config.nresponses++;
597 break; 615 break;
598 case 'c': /* critical time threshold */ 616 case 'c': /* critical time threshold */
599 if (!is_nonnegative (optarg)) 617 if (!is_nonnegative(optarg)) {
600 usage4 (_("Critical time must be a positive")); 618 usage4(_("Critical time must be a positive"));
601 else { 619 } else {
602 critical_time = strtod (optarg, NULL); 620 result.config.critical_time = strtod(optarg, NULL);
603 check_critical_time = true; 621 result.config.check_critical_time = true;
604 } 622 }
605 break; 623 break;
606 case 'w': /* warning time threshold */ 624 case 'w': /* warning time threshold */
607 if (!is_nonnegative (optarg)) 625 if (!is_nonnegative(optarg)) {
608 usage4 (_("Warning time must be a positive")); 626 usage4(_("Warning time must be a positive"));
609 else { 627 } else {
610 warning_time = strtod (optarg, NULL); 628 result.config.warning_time = strtod(optarg, NULL);
611 check_warning_time = true; 629 result.config.check_warning_time = true;
612 } 630 }
613 break; 631 break;
614 case 'v': /* verbose */ 632 case 'v': /* verbose */
615 verbose++; 633 verbose++;
616 break; 634 break;
617 case 'q': 635 case 'q':
618 ignore_send_quit_failure = true; /* ignore problem sending QUIT */ 636 result.config.ignore_send_quit_failure = true; /* ignore problem sending QUIT */
619 break; 637 break;
620 case 't': /* timeout */ 638 case 't': /* timeout */
621 if (is_intnonneg (optarg)) { 639 if (is_intnonneg(optarg)) {
622 socket_timeout = atoi (optarg); 640 socket_timeout = atoi(optarg);
623 } 641 } else {
624 else { 642 usage4(_("Timeout interval must be a positive integer"));
625 usage4 (_("Timeout interval must be a positive integer"));
626 } 643 }
627 break; 644 break;
628 case 'D': 645 case 'D': {
629 /* Check SSL cert validity */ 646 /* Check SSL cert validity */
630#ifdef USE_OPENSSL 647#ifdef USE_OPENSSL
631 if ((temp=strchr(optarg,','))!=NULL) { 648 char *temp;
632 *temp='\0'; 649 if ((temp = strchr(optarg, ',')) != NULL) {
633 if (!is_intnonneg (optarg)) 650 *temp = '\0';
634 usage2 ("Invalid certificate expiration period", optarg); 651 if (!is_intnonneg(optarg)) {
635 days_till_exp_warn = atoi(optarg); 652 usage2("Invalid certificate expiration period", optarg);
636 *temp=','; 653 }
637 temp++; 654 result.config.days_till_exp_warn = atoi(optarg);
638 if (!is_intnonneg (temp)) 655 *temp = ',';
639 usage2 (_("Invalid certificate expiration period"), temp); 656 temp++;
640 days_till_exp_crit = atoi (temp); 657 if (!is_intnonneg(temp)) {
641 } 658 usage2(_("Invalid certificate expiration period"), temp);
642 else { 659 }
643 days_till_exp_crit=0; 660 result.config.days_till_exp_crit = atoi(temp);
644 if (!is_intnonneg (optarg)) 661 } else {
645 usage2 ("Invalid certificate expiration period", optarg); 662 result.config.days_till_exp_crit = 0;
646 days_till_exp_warn = atoi (optarg); 663 if (!is_intnonneg(optarg)) {
647 } 664 usage2("Invalid certificate expiration period", optarg);
648 check_cert = true; 665 }
649 ignore_send_quit_failure = true; 666 result.config.days_till_exp_warn = atoi(optarg);
667 }
668 result.config.check_cert = true;
669 result.config.ignore_send_quit_failure = true;
650#else 670#else
651 usage (_("SSL support not available - install OpenSSL and recompile")); 671 usage(_("SSL support not available - install OpenSSL and recompile"));
652#endif 672#endif
653 implicit_tls = true; 673 implicit_tls = true;
654 // fallthrough 674 // fallthrough
655 case 's': 675 case 's':
656 /* ssl */ 676 /* ssl */
657 use_ssl = true; 677 result.config.use_ssl = true;
658 server_port = SMTPS_PORT; 678 result.config.server_port = SMTPS_PORT;
659 break; 679 break;
660 case 'S': 680 case 'S':
661 /* starttls */ 681 /* starttls */
662 use_starttls = true; 682 result.config.use_starttls = true;
663 use_ehlo = true; 683 result.config.use_ehlo = true;
664 break; 684 break;
685 }
665 case SNI_OPTION: 686 case SNI_OPTION:
666#ifdef HAVE_SSL 687#ifdef HAVE_SSL
667 use_sni = true; 688 result.config.use_sni = true;
668#else 689#else
669 usage (_("SSL support not available - install OpenSSL and recompile")); 690 usage(_("SSL support not available - install OpenSSL and recompile"));
670#endif 691#endif
671 break; 692 break;
672 case 'r': 693 case 'r':
673 use_proxy_prefix = true; 694 result.config.use_proxy_prefix = true;
674 break; 695 break;
675 case 'L': 696 case 'L':
676 use_lhlo = true; 697 result.config.use_lhlo = true;
677 break; 698 break;
678 case '4': 699 case '4':
679 address_family = AF_INET; 700 address_family = AF_INET;
@@ -682,102 +703,81 @@ process_arguments (int argc, char **argv)
682#ifdef USE_IPV6 703#ifdef USE_IPV6
683 address_family = AF_INET6; 704 address_family = AF_INET6;
684#else 705#else
685 usage4 (_("IPv6 support not available")); 706 usage4(_("IPv6 support not available"));
686#endif 707#endif
687 break; 708 break;
688 case 'V': /* version */ 709 case 'V': /* version */
689 print_revision (progname, NP_VERSION); 710 print_revision(progname, NP_VERSION);
690 exit (STATE_UNKNOWN); 711 exit(STATE_UNKNOWN);
691 case 'h': /* help */ 712 case 'h': /* help */
692 print_help (); 713 print_help();
693 exit (STATE_UNKNOWN); 714 exit(STATE_UNKNOWN);
694 case '?': /* help */ 715 case '?': /* help */
695 usage5 (); 716 usage5();
696 } 717 }
697 } 718 }
698 719
699 c = optind; 720 int c = optind;
700 if (server_address == NULL) { 721 if (result.config.server_address == NULL) {
701 if (argv[c]) { 722 if (argv[c]) {
702 if (is_host (argv[c])) 723 if (is_host(argv[c])) {
703 server_address = argv[c]; 724 result.config.server_address = argv[c];
704 else 725 } else {
705 usage2 (_("Invalid hostname/address"), argv[c]); 726 usage2(_("Invalid hostname/address"), argv[c]);
706 } 727 }
707 else { 728 } else {
708 xasprintf (&server_address, "127.0.0.1"); 729 result.config.server_address = strdup("localhost");
709 } 730 }
710 } 731 }
711 732
712 if (server_expect == NULL) 733 if (result.config.use_starttls && result.config.use_ssl) {
713 server_expect = strdup (SMTP_EXPECT);
714
715 if (mail_command == NULL)
716 mail_command = strdup("MAIL ");
717
718 if (from_arg==NULL)
719 from_arg = strdup(" ");
720
721 if (use_starttls && use_ssl) {
722 if (implicit_tls) { 734 if (implicit_tls) {
723 use_ssl = false; 735 result.config.use_ssl = false;
724 server_port = SMTP_PORT;
725 } else { 736 } else {
726 usage4 (_("Set either -s/--ssl/--tls or -S/--starttls")); 737 usage4(_("Set either -s/--ssl/--tls or -S/--starttls"));
727 } 738 }
728 } 739 }
729 740
730 if (server_port_option != 0) { 741 if (server_port_option != 0) {
731 server_port = server_port_option; 742 result.config.server_port = server_port_option;
732 } 743 }
733 744
734 return validate_arguments (); 745 return result;
735}
736
737
738
739int
740validate_arguments (void)
741{
742 return OK;
743} 746}
744 747
745 748char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int socket_descriptor,
746void 749 bool ssl_established) {
747smtp_quit(void) 750 int sent_bytes =
748{ 751 my_send(config, SMTP_QUIT, strlen(SMTP_QUIT), socket_descriptor, ssl_established);
749 int bytes; 752 if (sent_bytes < 0) {
750 int n; 753 if (config.ignore_send_quit_failure) {
751 754 if (verbose) {
752 n = my_send(SMTP_QUIT, strlen(SMTP_QUIT));
753 if(n < 0) {
754 if(ignore_send_quit_failure) {
755 if(verbose) {
756 printf(_("Connection closed by server before sending QUIT command\n")); 755 printf(_("Connection closed by server before sending QUIT command\n"));
757 } 756 }
758 return; 757 return buffer;
759 } 758 }
760 die (STATE_UNKNOWN, 759 die(STATE_UNKNOWN, _("Connection closed by server before sending QUIT command\n"));
761 _("Connection closed by server before sending QUIT command\n"));
762 } 760 }
763 761
764 if (verbose) 762 if (verbose) {
765 printf(_("sent %s\n"), "QUIT"); 763 printf(_("sent %s\n"), "QUIT");
764 }
766 765
767 /* read the response but don't care about problems */ 766 /* read the response but don't care about problems */
768 bytes = recvlines(buffer, MAX_INPUT_BUFFER); 767 int bytes = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established);
769 if (verbose) { 768 if (verbose) {
770 if (bytes < 0) 769 if (bytes < 0) {
771 printf(_("recv() failed after QUIT.")); 770 printf(_("recv() failed after QUIT."));
772 else if (bytes == 0) 771 } else if (bytes == 0) {
773 printf(_("Connection reset by peer.")); 772 printf(_("Connection reset by peer."));
774 else { 773 } else {
775 buffer[bytes] = '\0'; 774 buffer[bytes] = '\0';
776 printf(_("received %s\n"), buffer); 775 printf(_("received %s\n"), buffer);
777 } 776 }
778 } 777 }
779}
780 778
779 return buffer;
780}
781 781
782/* 782/*
783 * Receive one line, copy it into buf and nul-terminate it. Returns the 783 * Receive one line, copy it into buf and nul-terminate it. Returns the
@@ -788,24 +788,23 @@ smtp_quit(void)
788 * function which buffers the data, move that to netutils.c and change 788 * function which buffers the data, move that to netutils.c and change
789 * check_smtp and other plugins to use that. Also, remove (\r)\n. 789 * check_smtp and other plugins to use that. Also, remove (\r)\n.
790 */ 790 */
791int 791int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
792recvline(char *buf, size_t bufsize) 792 bool ssl_established) {
793{
794 int result; 793 int result;
795 unsigned i; 794 int counter;
796 795
797 for (i = result = 0; i < bufsize - 1; i++) { 796 for (counter = result = 0; counter < bufsize - 1; counter++) {
798 if ((result = my_recv(&buf[i], 1)) != 1) 797 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
799 break; 798 break;
800 if (buf[i] == '\n') { 799 }
801 buf[++i] = '\0'; 800 if (buf[counter] == '\n') {
802 return i; 801 buf[++counter] = '\0';
802 return counter;
803 } 803 }
804 } 804 }
805 return (result == 1 || i == 0) ? -2 : result; /* -2 if out of space */ 805 return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */
806} 806}
807 807
808
809/* 808/*
810 * Receive one or more lines, copy them into buf and nul-terminate it. Returns 809 * Receive one or more lines, copy them into buf and nul-terminate it. Returns
811 * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on 810 * the number of bytes written to buf (excluding the '\0') or 0 on EOF or <0 on
@@ -820,117 +819,110 @@ recvline(char *buf, size_t bufsize)
820 * 819 *
821 * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n. 820 * TODO: Move this to netutils.c. Also, remove \r and possibly the final \n.
822 */ 821 */
823int 822int recvlines(check_smtp_config config, char *buf, size_t bufsize, int socket_descriptor,
824recvlines(char *buf, size_t bufsize) 823 bool ssl_established) {
825{ 824 int result;
826 int result, i; 825 int counter;
827 826
828 for (i = 0; /* forever */; i += result) 827 for (counter = 0; /* forever */; counter += result) {
829 if (!((result = recvline(buf + i, bufsize - i)) > 3 && 828 if (!((result = recvline(buf + counter, bufsize - counter, config, socket_descriptor,
830 isdigit((int)buf[i]) && 829 ssl_established)) > 3 &&
831 isdigit((int)buf[i + 1]) && 830 isdigit((int)buf[counter]) && isdigit((int)buf[counter + 1]) &&
832 isdigit((int)buf[i + 2]) && 831 isdigit((int)buf[counter + 2]) && buf[counter + 3] == '-')) {
833 buf[i + 3] == '-'))
834 break; 832 break;
833 }
834 }
835 835
836 return (result <= 0) ? result : result + i; 836 return (result <= 0) ? result : result + counter;
837} 837}
838 838
839 839int my_close(int socket_descriptor) {
840int
841my_close (void)
842{
843 int result; 840 int result;
844 result = close(sd); 841 result = close(socket_descriptor);
845#ifdef HAVE_SSL 842#ifdef HAVE_SSL
846 np_net_ssl_cleanup(); 843 np_net_ssl_cleanup();
847#endif 844#endif
848 return result; 845 return result;
849} 846}
850 847
851 848void print_help(void) {
852void
853print_help (void)
854{
855 char *myport; 849 char *myport;
856 xasprintf (&myport, "%d", SMTP_PORT); 850 xasprintf(&myport, "%d", SMTP_PORT);
857 851
858 print_revision (progname, NP_VERSION); 852 print_revision(progname, NP_VERSION);
859 853
860 printf ("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n"); 854 printf("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n");
861 printf (COPYRIGHT, copyright, email); 855 printf(COPYRIGHT, copyright, email);
862 856
863 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host.")); 857 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host."));
864 858
865 printf ("\n\n"); 859 printf("\n\n");
866 860
867 print_usage (); 861 print_usage();
868 862
869 printf (UT_HELP_VRSN); 863 printf(UT_HELP_VRSN);
870 printf (UT_EXTRA_OPTS); 864 printf(UT_EXTRA_OPTS);
871 865
872 printf (UT_HOST_PORT, 'p', myport); 866 printf(UT_HOST_PORT, 'p', myport);
873 867
874 printf (UT_IPv46); 868 printf(UT_IPv46);
875 869
876 printf (" %s\n", "-e, --expect=STRING"); 870 printf(" %s\n", "-e, --expect=STRING");
877 printf (_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT); 871 printf(_(" String to expect in first line of server response (default: '%s')\n"),
878 printf (" %s\n", "-C, --command=STRING"); 872 SMTP_EXPECT);
879 printf (" %s\n", _("SMTP command (may be used repeatedly)")); 873 printf(" %s\n", "-C, --command=STRING");
880 printf (" %s\n", "-R, --response=STRING"); 874 printf(" %s\n", _("SMTP command (may be used repeatedly)"));
881 printf (" %s\n", _("Expected response to command (may be used repeatedly)")); 875 printf(" %s\n", "-R, --response=STRING");
882 printf (" %s\n", "-f, --from=STRING"); 876 printf(" %s\n", _("Expected response to command (may be used repeatedly)"));
883 printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")), 877 printf(" %s\n", "-f, --from=STRING");
884 printf (" %s\n", "-F, --fqdn=STRING"); 878 printf(" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")),
885 printf (" %s\n", _("FQDN used for HELO")); 879 printf(" %s\n", "-F, --fqdn=STRING");
886 printf (" %s\n", "-r, --proxy"); 880 printf(" %s\n", _("FQDN used for HELO"));
887 printf (" %s\n", _("Use PROXY protocol prefix for the connection.")); 881 printf(" %s\n", "-r, --proxy");
882 printf(" %s\n", _("Use PROXY protocol prefix for the connection."));
888#ifdef HAVE_SSL 883#ifdef HAVE_SSL
889 printf (" %s\n", "-D, --certificate=INTEGER[,INTEGER]"); 884 printf(" %s\n", "-D, --certificate=INTEGER[,INTEGER]");
890 printf (" %s\n", _("Minimum number of days a certificate has to be valid.")); 885 printf(" %s\n", _("Minimum number of days a certificate has to be valid."));
891 printf (" %s\n", "-s, --ssl, --tls"); 886 printf(" %s\n", "-s, --ssl, --tls");
892 printf (" %s\n", _("Use SSL/TLS for the connection.")); 887 printf(" %s\n", _("Use SSL/TLS for the connection."));
893 printf (_(" Sets default port to %d.\n"), SMTPS_PORT); 888 printf(_(" Sets default port to %d.\n"), SMTPS_PORT);
894 printf (" %s\n", "-S, --starttls"); 889 printf(" %s\n", "-S, --starttls");
895 printf (" %s\n", _("Use STARTTLS for the connection.")); 890 printf(" %s\n", _("Use STARTTLS for the connection."));
896 printf (" %s\n", "--sni"); 891 printf(" %s\n", "--sni");
897 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 892 printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
898#endif 893#endif
899 894
900 printf (" %s\n", "-A, --authtype=STRING"); 895 printf(" %s\n", "-A, --authtype=STRING");
901 printf (" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)")); 896 printf(" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)"));
902 printf (" %s\n", "-U, --authuser=STRING"); 897 printf(" %s\n", "-U, --authuser=STRING");
903 printf (" %s\n", _("SMTP AUTH username")); 898 printf(" %s\n", _("SMTP AUTH username"));
904 printf (" %s\n", "-P, --authpass=STRING"); 899 printf(" %s\n", "-P, --authpass=STRING");
905 printf (" %s\n", _("SMTP AUTH password")); 900 printf(" %s\n", _("SMTP AUTH password"));
906 printf (" %s\n", "-L, --lmtp"); 901 printf(" %s\n", "-L, --lmtp");
907 printf (" %s\n", _("Send LHLO instead of HELO/EHLO")); 902 printf(" %s\n", _("Send LHLO instead of HELO/EHLO"));
908 printf (" %s\n", "-q, --ignore-quit-failure"); 903 printf(" %s\n", "-q, --ignore-quit-failure");
909 printf (" %s\n", _("Ignore failure when sending QUIT command to server")); 904 printf(" %s\n", _("Ignore failure when sending QUIT command to server"));
910
911 printf (UT_WARN_CRIT);
912 905
913 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 906 printf(UT_WARN_CRIT);
914 907
915 printf (UT_VERBOSE); 908 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
909
910 printf(UT_VERBOSE);
916 911
917 printf("\n"); 912 printf("\n");
918 printf ("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return")); 913 printf("%s\n", _("Successful connects return STATE_OK, refusals and timeouts return"));
919 printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful")); 914 printf("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful"));
920 printf ("%s\n", _("connects, but incorrect response messages from the host result in")); 915 printf("%s\n", _("connects, but incorrect response messages from the host result in"));
921 printf ("%s\n", _("STATE_WARNING return values.")); 916 printf("%s\n", _("STATE_WARNING return values."));
922 917
923 printf (UT_SUPPORT); 918 printf(UT_SUPPORT);
924} 919}
925 920
926 921void print_usage(void) {
927 922 printf("%s\n", _("Usage:"));
928void 923 printf("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n",
929print_usage (void) 924 progname);
930{ 925 printf("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
931 printf ("%s\n", _("Usage:")); 926 printf("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] "
932 printf ("%s -H host [-p port] [-4|-6] [-e expect] [-C command] [-R response] [-f from addr]\n", progname); 927 "[-v] \n");
933 printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout] [-q]\n");
934 printf ("[-F fqdn] [-S] [-L] [-D warn days cert expire[,crit days cert expire]] [-r] [--sni] [-v] \n");
935} 928}
936
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h
new file mode 100644
index 00000000..0a6511ef
--- /dev/null
+++ b/plugins/check_smtp.d/config.h
@@ -0,0 +1,92 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5#include <string.h>
6
7enum {
8 SMTP_PORT = 25,
9 SMTPS_PORT = 465
10};
11
12#define SMTP_EXPECT "220"
13
14typedef struct {
15 int server_port;
16 char *server_address;
17 char *localhostname;
18 char *server_expect;
19 bool ignore_send_quit_failure;
20
21 double warning_time;
22 bool check_warning_time;
23 double critical_time;
24 bool check_critical_time;
25 bool use_ehlo;
26 bool use_lhlo;
27
28 char *from_arg;
29 bool send_mail_from;
30
31 int ncommands;
32 char **commands;
33
34 int nresponses;
35 char **responses;
36
37 char *authtype;
38 char *authuser;
39 char *authpass;
40
41 bool use_proxy_prefix;
42#ifdef HAVE_SSL
43 bool check_cert;
44 int days_till_exp_warn;
45 int days_till_exp_crit;
46 bool use_ssl;
47 bool use_starttls;
48 bool use_sni;
49#endif
50} check_smtp_config;
51
52check_smtp_config check_smtp_config_init() {
53 check_smtp_config tmp = {
54 .server_port = SMTP_PORT,
55 .server_address = NULL,
56 .localhostname = NULL,
57
58 .server_expect = SMTP_EXPECT,
59 .ignore_send_quit_failure = false,
60
61 .warning_time = 0,
62 .check_warning_time = false,
63 .critical_time = 0,
64 .check_critical_time = false,
65 .use_ehlo = false,
66 .use_lhlo = false,
67
68 .from_arg = strdup(" "),
69 .send_mail_from = false,
70
71 .ncommands = 0,
72 .commands = NULL,
73
74 .nresponses = 0,
75 .responses = NULL,
76
77 .authtype = NULL,
78 .authuser = NULL,
79 .authpass = NULL,
80
81 .use_proxy_prefix = false,
82#ifdef HAVE_SSL
83 .check_cert = false,
84 .days_till_exp_warn = 0,
85 .days_till_exp_crit = 0,
86 .use_ssl = false,
87 .use_starttls = false,
88 .use_sni = false,
89#endif
90 };
91 return tmp;
92}
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c
index c1d8e2dd..a5a7afe8 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -32,716 +32,494 @@ const char *progname = "check_snmp";
32const char *copyright = "1999-2024"; 32const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "runcmd.h" 36#include "./runcmd.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_cmd.h" 38#include "../lib/states.h"
39 39
40#define DEFAULT_COMMUNITY "public" 40#include "../lib/utils_base.h"
41#define DEFAULT_PORT "161" 41#include "../lib/output.h"
42#define DEFAULT_MIBLIST "ALL" 42#include "check_snmp.d/check_snmp_helpers.h"
43#define DEFAULT_PROTOCOL "1" 43
44#define DEFAULT_RETRIES 5 44#include <bits/getopt_core.h>
45#define DEFAULT_AUTH_PROTOCOL "MD5" 45#include <bits/getopt_ext.h>
46#define DEFAULT_PRIV_PROTOCOL "DES" 46#include <strings.h>
47#define DEFAULT_DELIMITER "=" 47#include <stdint.h>
48#define DEFAULT_OUTPUT_DELIMITER " " 48
49#define DEFAULT_BUFFER_SIZE 100 49#include "check_snmp.d/config.h"
50 50#include <stdlib.h>
51#define mark(a) ((a) != 0 ? "*" : "") 51#include <arpa/inet.h>
52 52#include <net-snmp/library/parse.h>
53#define CHECK_UNDEF 0 53#include <net-snmp/net-snmp-config.h>
54#define CRIT_PRESENT 1 54#include <net-snmp/net-snmp-includes.h>
55#define CRIT_STRING 2 55#include <net-snmp/library/snmp.h>
56#define CRIT_REGEX 4 56#include <net-snmp/library/keytools.h>
57#define WARN_PRESENT 8 57#include <net-snmp/library/snmp_api.h>
58 58#include <net-snmp/session_api.h>
59#define OID_COUNT_STEP 8 59#include <net-snmp/definitions.h>
60 60#include <net-snmp/library/asn1.h>
61/* Longopts only arguments */ 61#include <net-snmp/mib_api.h>
62#define L_CALCULATE_RATE CHAR_MAX + 1 62#include <net-snmp/library/snmp_impl.h>
63#define L_RATE_MULTIPLIER CHAR_MAX + 2 63#include <string.h>
64#define L_INVERT_SEARCH CHAR_MAX + 3 64#include "../gl/regex.h"
65#define L_OFFSET CHAR_MAX + 4 65#include "../gl/base64.h"
66#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 66#include <assert.h>
67 67
68/* Gobble to string - stop incrementing c when c[0] match one of the 68const char DEFAULT_COMMUNITY[] = "public";
69 * characters in s */ 69const char DEFAULT_MIBLIST[] = "ALL";
70#define GOBBLE_TOS(c, s) \ 70#define DEFAULT_AUTH_PROTOCOL "MD5"
71 while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ 71
72 c++; \ 72#ifdef HAVE_USM_DES_PRIV_PROTOCOL
73# define DEFAULT_PRIV_PROTOCOL "DES"
74#else
75# define DEFAULT_PRIV_PROTOCOL "AES"
76#endif
77
78typedef struct proces_arguments_wrapper {
79 int errorcode;
80 check_snmp_config config;
81} process_arguments_wrapper;
82
83static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
84static char *trim_whitespaces_and_check_quoting(char *str);
85static char *get_next_argument(char *str);
86void print_usage(void);
87void print_help(void);
88
89int verbose = 0;
90
91typedef struct {
92 int errorcode;
93 char *state_string;
94} gen_state_string_type;
95gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) {
96 char *encoded_string = NULL;
97 gen_state_string_type result = {.errorcode = OK, .state_string = NULL};
98
99 if (verbose > 1) {
100 printf("%s:\n", __FUNCTION__);
101 for (size_t i = 0; i < num_of_entries; i++) {
102 printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp));
103 switch (entries[i].type) {
104 case ASN_GAUGE:
105 printf("Type GAUGE\n");
106 break;
107 case ASN_TIMETICKS:
108 printf("Type TIMETICKS\n");
109 break;
110 case ASN_COUNTER:
111 printf("Type COUNTER\n");
112 break;
113 case ASN_UINTEGER:
114 printf("Type UINTEGER\n");
115 break;
116 case ASN_COUNTER64:
117 printf("Type COUNTER64\n");
118 break;
119 case ASN_FLOAT:
120 printf("Type FLOAT\n");
121 case ASN_DOUBLE:
122 printf("Type DOUBLE\n");
123 break;
124 case ASN_INTEGER:
125 printf("Type INTEGER\n");
126 break;
127 }
128
129 switch (entries[i].type) {
130 case ASN_GAUGE:
131 case ASN_TIMETICKS:
132 case ASN_COUNTER:
133 case ASN_UINTEGER:
134 case ASN_COUNTER64:
135 printf("Value %llu\n", entries[i].value.uIntVal);
136 break;
137 case ASN_FLOAT:
138 case ASN_DOUBLE:
139 printf("Value %f\n", entries[i].value.doubleVal);
140 break;
141 case ASN_INTEGER:
142 printf("Value %lld\n", entries[i].value.intVal);
143 break;
144 }
145 }
146 }
147
148 idx_t encoded = base64_encode_alloc((const char *)entries,
149 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
150 &encoded_string);
151
152 if (encoded > 0 && encoded_string != NULL) {
153 // success
154 if (verbose > 1) {
155 printf("encoded string: %s\n", encoded_string);
156 printf("encoded string length: %lu\n", strlen(encoded_string));
157 }
158 result.state_string = encoded_string;
159 return result;
73 } 160 }
74/* Given c, keep track of backslashes (bk) and double-quotes (dq) 161 result.errorcode = ERROR;
75 * from c[0] */ 162 return result;
76#define COUNT_SEQ(c, bk, dq) \ 163}
77 switch (c[0]) { \ 164
78 case '\\': \ 165typedef struct {
79 if (bk) \ 166 int errorcode;
80 bk--; \ 167 check_snmp_state_entry *state;
81 else \ 168} recover_state_data_type;
82 bk++; \ 169recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
83 break; \ 170 recover_state_data_type result = {.errorcode = OK, .state = NULL};
84 case '"': \ 171
85 if (!dq) { \ 172 if (verbose > 1) {
86 dq++; \ 173 printf("%s:\n", __FUNCTION__);
87 } else if (!bk) { \ 174 printf("State string: %s\n", state_string);
88 dq--; \ 175 printf("State string length: %lu\n", state_string_length);
89 } else { \
90 bk--; \
91 } \
92 break; \
93 } 176 }
94 177
95static int process_arguments(int, char **); 178 idx_t outlen = 0;
96static int validate_arguments(void); 179 bool decoded =
97static char *thisarg(char *str); 180 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
98static char *nextarg(char *str); 181
99void print_usage(void); 182 if (!decoded) {
100static void print_help(void); 183 if (verbose) {
101static char *multiply(char *str); 184 printf("Failed to decode state string\n");
102 185 }
103#include "regex.h" 186 // failure to decode
104static char regex_expect[MAX_INPUT_BUFFER] = ""; 187 result.errorcode = ERROR;
105static regex_t preg; 188 return result;
106static regmatch_t pmatch[10]; 189 }
107static char errbuf[MAX_INPUT_BUFFER] = ""; 190
108static char perfstr[MAX_INPUT_BUFFER] = "| "; 191 if (result.state == NULL) {
109static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 192 // Memory Error?
110static int eflags = 0; 193 result.errorcode = ERROR;
111static int errcode, excode; 194 return result;
112 195 }
113static char *server_address = NULL; 196
114static char *community = NULL; 197 if (verbose > 1) {
115static char **contextargs = NULL; 198 printf("Recovered %lu entries of size %lu\n",
116static char *context = NULL; 199 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
117static char **authpriv = NULL; 200
118static char *proto = NULL; 201 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
119static char *seclevel = NULL; 202 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
120static char *secname = NULL; 203 ctime(&result.state[i].timestamp));
121static char *authproto = NULL; 204 switch (result.state[i].type) {
122static char *privproto = NULL; 205 case ASN_GAUGE:
123static char *authpasswd = NULL; 206 printf("Type GAUGE\n");
124static char *privpasswd = NULL; 207 break;
125static int nulloid = STATE_UNKNOWN; 208 case ASN_TIMETICKS:
126static char **oids = NULL; 209 printf("Type TIMETICKS\n");
127static size_t oids_size = 0; 210 break;
128static char *label; 211 case ASN_COUNTER:
129static char *units; 212 printf("Type COUNTER\n");
130static char *port; 213 break;
131static char *snmpcmd; 214 case ASN_UINTEGER:
132static char string_value[MAX_INPUT_BUFFER] = ""; 215 printf("Type UINTEGER\n");
133static int invert_search = 0; 216 break;
134static char **labels = NULL; 217 case ASN_COUNTER64:
135static char **unitv = NULL; 218 printf("Type COUNTER64\n");
136static size_t nlabels = 0; 219 break;
137static size_t labels_size = OID_COUNT_STEP; 220 case ASN_FLOAT:
138static size_t nunits = 0; 221 printf("Type FLOAT\n");
139static size_t unitv_size = OID_COUNT_STEP; 222 case ASN_DOUBLE:
140static size_t numoids = 0; 223 printf("Type DOUBLE\n");
141static int numauthpriv = 0; 224 break;
142static int numcontext = 0; 225 case ASN_INTEGER:
143static int verbose = 0; 226 printf("Type INTEGER\n");
144static bool usesnmpgetnext = false; 227 break;
145static char *warning_thresholds = NULL; 228 }
146static char *critical_thresholds = NULL; 229
147static thresholds **thlds; 230 switch (result.state[i].type) {
148static size_t thlds_size = OID_COUNT_STEP; 231 case ASN_GAUGE:
149static double *response_value; 232 case ASN_TIMETICKS:
150static size_t response_size = OID_COUNT_STEP; 233 case ASN_COUNTER:
151static int retries = 0; 234 case ASN_UINTEGER:
152static int *eval_method; 235 case ASN_COUNTER64:
153static size_t eval_size = OID_COUNT_STEP; 236 printf("Value %llu\n", result.state[i].value.uIntVal);
154static char *delimiter; 237 break;
155static char *output_delim; 238 case ASN_FLOAT:
156static char *miblist = NULL; 239 case ASN_DOUBLE:
157static bool needmibs = false; 240 printf("Value %f\n", result.state[i].value.doubleVal);
158static int calculate_rate = 0; 241 break;
159static double offset = 0.0; 242 case ASN_INTEGER:
160static int rate_multiplier = 1; 243 printf("Value %lld\n", result.state[i].value.intVal);
161static state_data *previous_state; 244 break;
162static double *previous_value; 245 }
163static size_t previous_size = OID_COUNT_STEP; 246 }
164static int perf_labels = 1; 247 }
165static char *ip_version = ""; 248
166static double multiplier = 1.0; 249 return result;
167static char *fmtstr = "";
168static bool fmtstr_set = false;
169static char buffer[DEFAULT_BUFFER_SIZE];
170static bool ignore_mib_parsing_errors = false;
171
172static char *fix_snmp_range(char *th) {
173 double left;
174 double right;
175 char *colon;
176 char *ret;
177
178 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
179 return th;
180
181 left = strtod(th, NULL);
182 right = strtod(colon + 1, NULL);
183 if (right >= left)
184 return th;
185
186 if ((ret = malloc(strlen(th) + 2)) == NULL)
187 die(STATE_UNKNOWN, _("Cannot malloc"));
188 *colon = '\0';
189 sprintf(ret, "@%s:%s", colon + 1, th);
190 free(th);
191 return ret;
192} 250}
193 251
194int main(int argc, char **argv) { 252int main(int argc, char **argv) {
195 int len;
196 int total_oids;
197 size_t line;
198 unsigned int bk_count = 0;
199 unsigned int dq_count = 0;
200 int iresult = STATE_UNKNOWN;
201 int result = STATE_UNKNOWN;
202 int return_code = 0;
203 int external_error = 0;
204 char **command_line = NULL;
205 char *cl_hidden_auth = NULL;
206 char *oidname = NULL;
207 char *response = NULL;
208 char *mult_resp = NULL;
209 char *outbuff;
210 char *ptr = NULL;
211 char *show = NULL;
212 char *th_warn = NULL;
213 char *th_crit = NULL;
214 char type[8] = "";
215 output chld_out;
216 output chld_err;
217 char *previous_string = NULL;
218 char *ap = NULL;
219 char *state_string = NULL;
220 size_t response_length;
221 size_t current_length;
222 size_t string_length;
223 char *temp_string = NULL;
224 char *quote_string = NULL;
225 time_t current_time;
226 double temp_double;
227 time_t duration;
228 char *conv = "12345678";
229 int is_counter = 0;
230
231 setlocale(LC_ALL, ""); 253 setlocale(LC_ALL, "");
232 bindtextdomain(PACKAGE, LOCALEDIR); 254 bindtextdomain(PACKAGE, LOCALEDIR);
233 textdomain(PACKAGE); 255 textdomain(PACKAGE);
234 256
235 labels = malloc(labels_size * sizeof(*labels));
236 unitv = malloc(unitv_size * sizeof(*unitv));
237 thlds = malloc(thlds_size * sizeof(*thlds));
238 response_value = malloc(response_size * sizeof(*response_value));
239 previous_value = malloc(previous_size * sizeof(*previous_value));
240 eval_method = calloc(eval_size, sizeof(*eval_method));
241 oids = calloc(oids_size, sizeof(char *));
242
243 label = strdup("SNMP");
244 units = strdup("");
245 port = strdup(DEFAULT_PORT);
246 outbuff = strdup("");
247 delimiter = strdup(" = ");
248 output_delim = strdup(DEFAULT_OUTPUT_DELIMITER);
249 timeout_interval = DEFAULT_SOCKET_TIMEOUT; 257 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
250 retries = DEFAULT_RETRIES;
251 258
252 np_init((char *)progname, argc, argv); 259 np_init((char *)progname, argc, argv);
253 260
261 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
262
254 /* Parse extra opts if any */ 263 /* Parse extra opts if any */
255 argv = np_extra_opts(&argc, argv, progname); 264 argv = np_extra_opts(&argc, argv, progname);
256 265
257 np_set_args(argc, argv); 266 np_set_args(argc, argv);
258 267
259 time(&current_time); 268 // Initialize net-snmp before touching the session we are going to use
269 init_snmp("check_snmp");
260 270
261 if (process_arguments(argc, argv) == ERROR) 271 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
272 if (paw_tmp.errorcode == ERROR) {
262 usage4(_("Could not parse arguments")); 273 usage4(_("Could not parse arguments"));
263
264 if (calculate_rate) {
265 if (!strcmp(label, "SNMP"))
266 label = strdup("SNMP RATE");
267
268 size_t i = 0;
269
270 previous_state = np_state_read();
271 if (previous_state != NULL) {
272 /* Split colon separated values */
273 previous_string = strdup((char *)previous_state->data);
274 while ((ap = strsep(&previous_string, ":")) != NULL) {
275 if (verbose > 2)
276 printf("State for %zd=%s\n", i, ap);
277 while (i >= previous_size) {
278 previous_size += OID_COUNT_STEP;
279 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
280 }
281 previous_value[i++] = strtod(ap, NULL);
282 }
283 }
284 } 274 }
285 275
286 /* Populate the thresholds */ 276 check_snmp_config config = paw_tmp.config;
287 th_warn = warning_thresholds;
288 th_crit = critical_thresholds;
289 for (size_t i = 0; i < numoids; i++) {
290 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
291 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
292 /* translate "2:1" to "@1:2" for backwards compatibility */
293 w = w ? fix_snmp_range(w) : NULL;
294 c = c ? fix_snmp_range(c) : NULL;
295
296 while (i >= thlds_size) {
297 thlds_size += OID_COUNT_STEP;
298 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
299 }
300
301 /* Skip empty thresholds, while avoiding segfault */
302 set_thresholds(&thlds[i], w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
303 if (w) {
304 th_warn = strchr(th_warn, ',');
305 if (th_warn)
306 th_warn++;
307 free(w);
308 }
309 if (c) {
310 th_crit = strchr(th_crit, ',');
311 if (th_crit)
312 th_crit++;
313 free(c);
314 }
315 }
316 277
317 /* Create the command array to execute */ 278 if (config.output_format_is_set) {
318 if (usesnmpgetnext) { 279 mp_set_format(config.output_format);
319 snmpcmd = strdup(PATH_TO_SNMPGETNEXT);
320 } else {
321 snmpcmd = strdup(PATH_TO_SNMPGET);
322 } 280 }
323 281
324 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ 282 /* Set signal handling and alarm */
325 283 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
326 unsigned index = 0; 284 usage4(_("Cannot catch SIGALRM"));
327 command_line = calloc(11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof(char *));
328
329 command_line[index++] = snmpcmd;
330 command_line[index++] = strdup("-Le");
331 command_line[index++] = strdup("-t");
332 xasprintf(&command_line[index++], "%d", timeout_interval);
333 command_line[index++] = strdup("-r");
334 xasprintf(&command_line[index++], "%d", retries);
335 command_line[index++] = strdup("-m");
336 command_line[index++] = strdup(miblist);
337 command_line[index++] = "-v";
338 command_line[index++] = strdup(proto);
339
340 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''",
341 proto);
342
343 if (ignore_mib_parsing_errors) {
344 command_line[index++] = "-Pe";
345 xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth);
346 } 285 }
347 286
348 for (int i = 0; i < numcontext; i++) { 287 time_t current_time;
349 command_line[index++] = contextargs[i]; 288 time(&current_time);
350 }
351 289
352 for (int i = 0; i < numauthpriv; i++) { 290 if (verbose > 2) {
353 command_line[index++] = authpriv[i]; 291 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
354 } 292 }
355 293
356 xasprintf(&command_line[index++], "%s:%s", server_address, port); 294 snmp_responces response = do_snmp_query(config.snmp_params);
357 295
358 xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); 296 mp_check overall = mp_check_init();
359 297
360 for (size_t i = 0; i < numoids; i++) { 298 if (response.errorcode == OK) {
361 command_line[index++] = oids[i]; 299 mp_subcheck sc_successfull_query = mp_subcheck_init();
362 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); 300 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
301 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
302 mp_add_subcheck_to_check(&overall, sc_successfull_query);
303 } else {
304 // Error treatment here, either partial or whole
305 mp_subcheck sc_failed_query = mp_subcheck_init();
306 xasprintf(&sc_failed_query.output, "SNMP query failed");
307 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
308 mp_add_subcheck_to_check(&overall, sc_failed_query);
309 mp_exit(overall);
363 } 310 }
364 311
365 command_line[index++] = NULL; 312 check_snmp_state_entry *prev_state = NULL;
313 bool have_previous_state = false;
366 314
367 if (verbose) { 315 if (config.evaluation_params.calculate_rate) {
368 printf("%s\n", cl_hidden_auth); 316 state_data *previous_state = np_state_read(stateKey);
369 } 317 if (previous_state == NULL) {
318 // failed to recover state
319 // or no previous state
320 have_previous_state = false;
321 } else {
322 // sanity check
323 recover_state_data_type prev_state_wrapper =
324 recover_state_data(previous_state->data, (idx_t)previous_state->length);
370 325
371 /* Set signal handling and alarm */ 326 if (prev_state_wrapper.errorcode == OK) {
372 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 327 have_previous_state = true;
373 usage4(_("Cannot catch SIGALRM")); 328 prev_state = prev_state_wrapper.state;
374 } 329 } else {
375 alarm(timeout_interval * retries + 5); 330 have_previous_state = false;
376 331 prev_state = NULL;
377 /* Run the command */
378 return_code = cmd_run_array(command_line, &chld_out, &chld_err, 0);
379
380 /* disable alarm again */
381 alarm(0);
382
383 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
384 only return state unknown if return code is non zero or there is no stdout.
385 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
386 */
387 if (return_code != 0)
388 external_error = 1;
389 if (chld_out.lines == 0)
390 external_error = 1;
391 if (external_error) {
392 if (chld_err.lines > 0) {
393 printf(_("External command error: %s\n"), chld_err.line[0]);
394 for (size_t i = 1; i < chld_err.lines; i++) {
395 printf("%s\n", chld_err.line[i]);
396 } 332 }
397 } else {
398 printf(_("External command error with no output (return code: %d)\n"), return_code);
399 } 333 }
400 exit(STATE_UNKNOWN);
401 } 334 }
402 335
403 if (verbose) { 336 check_snmp_state_entry *new_state = NULL;
404 for (size_t i = 0; i < chld_out.lines; i++) { 337 if (config.evaluation_params.calculate_rate) {
405 printf("%s\n", chld_out.line[i]); 338 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
339 if (new_state == NULL) {
340 die(STATE_UNKNOWN, "memory allocation failed");
406 } 341 }
407 } 342 }
408 343
409 line = 0; 344 // We got the the query results, now process them
410 total_oids = 0; 345 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
411 for (size_t i = 0; line < chld_out.lines && i < numoids; line++, i++, total_oids++) { 346 if (verbose > 0) {
412 if (calculate_rate) 347 printf("loop_index: %zu\n", loop_index);
413 conv = "%.10g";
414 else
415 conv = "%.0f";
416
417 ptr = chld_out.line[line];
418 oidname = strpcpy(oidname, ptr, delimiter);
419 response = strstr(ptr, delimiter);
420 if (response == NULL)
421 break;
422
423 if (verbose > 2) {
424 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i + 1, line + 1, oidname, response);
425 } 348 }
426 349
427 /* Clean up type array - Sol10 does not necessarily zero it out */ 350 check_snmp_state_entry previous_unit_state = {};
428 bzero(type, sizeof(type)); 351 if (config.evaluation_params.calculate_rate && have_previous_state) {
429 352 previous_unit_state = prev_state[loop_index];
430 is_counter = 0; 353 }
431 /* We strip out the datatype indicator for PHBs */
432 if (strstr(response, "Gauge: ")) {
433 show = multiply(strstr(response, "Gauge: ") + 7);
434 } else if (strstr(response, "Gauge32: ")) {
435 show = multiply(strstr(response, "Gauge32: ") + 9);
436 } else if (strstr(response, "Counter32: ")) {
437 show = strstr(response, "Counter32: ") + 11;
438 is_counter = 1;
439 if (!calculate_rate)
440 strcpy(type, "c");
441 } else if (strstr(response, "Counter64: ")) {
442 show = strstr(response, "Counter64: ") + 11;
443 is_counter = 1;
444 if (!calculate_rate)
445 strcpy(type, "c");
446 } else if (strstr(response, "INTEGER: ")) {
447 show = multiply(strstr(response, "INTEGER: ") + 9);
448
449 if (fmtstr_set) {
450 conv = fmtstr;
451 }
452 } else if (strstr(response, "OID: ")) {
453 show = strstr(response, "OID: ") + 5;
454 } else if (strstr(response, "STRING: ")) {
455 show = strstr(response, "STRING: ") + 8;
456 conv = "%.10g";
457
458 /* Get the rest of the string on multi-line strings */
459 ptr = show;
460 COUNT_SEQ(ptr, bk_count, dq_count)
461 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
462 ptr++;
463 GOBBLE_TOS(ptr, "\n\"\\")
464 COUNT_SEQ(ptr, bk_count, dq_count)
465 }
466
467 if (dq_count) { /* unfinished line */
468 /* copy show verbatim first */
469 if (!mult_resp)
470 mult_resp = strdup("");
471 xasprintf(&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
472 /* then strip out unmatched double-quote from single-line output */
473 if (show[0] == '"')
474 show++;
475
476 /* Keep reading until we match end of double-quoted string */
477 for (line++; line < chld_out.lines; line++) {
478 ptr = chld_out.line[line];
479 xasprintf(&mult_resp, "%s%s\n", mult_resp, ptr);
480
481 COUNT_SEQ(ptr, bk_count, dq_count)
482 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
483 ptr++;
484 GOBBLE_TOS(ptr, "\n\"\\")
485 COUNT_SEQ(ptr, bk_count, dq_count)
486 }
487 /* Break for loop before next line increment when done */
488 if (!dq_count)
489 break;
490 }
491 }
492
493 } else if (strstr(response, "Timeticks: ")) {
494 show = strstr(response, "Timeticks: ");
495 } else
496 show = response + 3;
497 354
498 iresult = STATE_DEPENDENT; 355 check_snmp_evaluation single_eval =
356 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
357 config.snmp_params.test_units[loop_index], current_time,
358 previous_unit_state, have_previous_state);
499 359
500 /* Process this block for numeric comparisons */ 360 if (config.evaluation_params.calculate_rate &&
501 /* Make some special values,like Timeticks numeric only if a threshold is defined */ 361 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
502 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { 362 new_state[loop_index] = single_eval.state;
503 if (verbose > 2) {
504 print_thresholds(" thresholds", thlds[i]);
505 }
506 ptr = strpbrk(show, "-0123456789");
507 if (ptr == NULL) {
508 if (nulloid == 3)
509 die(STATE_UNKNOWN, _("No valid data returned (%s)\n"), show);
510 else if (nulloid == 0)
511 die(STATE_OK, _("No valid data returned (%s)\n"), show);
512 else if (nulloid == 1)
513 die(STATE_WARNING, _("No valid data returned (%s)\n"), show);
514 else if (nulloid == 2)
515 die(STATE_CRITICAL, _("No valid data returned (%s)\n"), show);
516 }
517 while (i >= response_size) {
518 response_size += OID_COUNT_STEP;
519 response_value = realloc(response_value, response_size * sizeof(*response_value));
520 }
521 response_value[i] = strtod(ptr, NULL) + offset;
522
523 if (calculate_rate) {
524 if (previous_state != NULL) {
525 duration = current_time - previous_state->time;
526 if (duration <= 0)
527 die(STATE_UNKNOWN, _("Time duration between plugin calls is invalid"));
528 temp_double = response_value[i] - previous_value[i];
529 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
530 if (is_counter) {
531 if (temp_double < (double)0.0)
532 temp_double += (double)4294967296.0; /* 2^32 */
533 if (temp_double < (double)0.0)
534 temp_double += (double)18446744069414584320.0; /* 2^64-2^32 */
535 ;
536 }
537 /* Convert to per second, then use multiplier */
538 temp_double = temp_double / duration * rate_multiplier;
539 iresult = get_status(temp_double, thlds[i]);
540 xasprintf(&show, conv, temp_double);
541 }
542 } else {
543 iresult = get_status(response_value[i], thlds[i]);
544 xasprintf(&show, conv, response_value[i]);
545 }
546 } 363 }
547 364
548 /* Process this block for string matching */ 365 mp_add_subcheck_to_check(&overall, single_eval.sc);
549 else if (eval_size > i && eval_method[i] & CRIT_STRING) { 366 }
550 if (strcmp(show, string_value))
551 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
552 else
553 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
554 }
555 367
556 /* Process this block for regex matching */ 368 if (config.evaluation_params.calculate_rate) {
557 else if (eval_size > i && eval_method[i] & CRIT_REGEX) { 369 // store state
558 excode = regexec(&preg, response, 10, pmatch, eflags); 370 gen_state_string_type current_state_wrapper =
559 if (excode == 0) { 371 gen_state_string(new_state, config.snmp_params.num_of_test_units);
560 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
561 } else if (excode != REG_NOMATCH) {
562 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
563 printf(_("Execute Error: %s\n"), errbuf);
564 exit(STATE_CRITICAL);
565 } else {
566 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
567 }
568 }
569 372
570 /* Process this block for existence-nonexistence checks */ 373 if (current_state_wrapper.errorcode == OK) {
571 /* TV: Should this be outside of this else block? */ 374 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
572 else { 375 } else {
573 if (eval_size > i && eval_method[i] & CRIT_PRESENT) 376 die(STATE_UNKNOWN, "failed to create state string");
574 iresult = STATE_CRITICAL;
575 else if (eval_size > i && eval_method[i] & WARN_PRESENT)
576 iresult = STATE_WARNING;
577 else if (response && iresult == STATE_DEPENDENT)
578 iresult = STATE_OK;
579 } 377 }
378 }
379 mp_exit(overall);
380}
580 381
581 /* Result is the worst outcome of all the OIDs tested */ 382/* process command-line arguments */
582 result = max_state(result, iresult); 383static process_arguments_wrapper process_arguments(int argc, char **argv) {
583 384 enum {
584 /* Prepend a label for this OID if there is one */ 385 /* Longopts only arguments */
585 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 386 invert_search_index = CHAR_MAX + 1,
586 xasprintf(&outbuff, "%s%s%s %s%s%s", outbuff, (i == 0) ? " " : output_delim, labels[i], mark(iresult), show, mark(iresult)); 387 offset_index,
587 else 388 ignore_mib_parsing_errors_index,
588 xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); 389 connection_prefix_index,
589 390 output_format_index,
590 /* Append a unit string for this OID if there is one */ 391 calculate_rate,
591 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) 392 rate_multiplier
592 xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); 393 };
593 394
594 /* Write perfdata with whatever can be parsed by strtod, if possible */ 395 static struct option longopts[] = {
595 ptr = NULL; 396 STD_LONG_OPTS,
596 strtod(show, &ptr); 397 {"community", required_argument, 0, 'C'},
597 if (ptr > show) { 398 {"oid", required_argument, 0, 'o'},
598 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 399 {"object", required_argument, 0, 'o'},
599 temp_string = labels[i]; 400 {"delimiter", required_argument, 0, 'd'},
600 else 401 {"nulloid", required_argument, 0, 'z'},
601 temp_string = oidname; 402 {"output-delimiter", required_argument, 0, 'D'},
602 if (strpbrk(temp_string, " ='\"") == NULL) { 403 {"string", required_argument, 0, 's'},
603 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 404 {"timeout", required_argument, 0, 't'},
604 } else { 405 {"regex", required_argument, 0, 'r'},
605 if (strpbrk(temp_string, "'") == NULL) { 406 {"ereg", required_argument, 0, 'r'},
606 quote_string = "'"; 407 {"eregi", required_argument, 0, 'R'},
607 } else { 408 {"label", required_argument, 0, 'l'},
608 quote_string = "\""; 409 {"units", required_argument, 0, 'u'},
609 } 410 {"port", required_argument, 0, 'p'},
610 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 411 {"retries", required_argument, 0, 'e'},
611 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 412 {"miblist", required_argument, 0, 'm'},
612 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 413 {"protocol", required_argument, 0, 'P'},
613 } 414 {"context", required_argument, 0, 'N'},
614 strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); 415 {"seclevel", required_argument, 0, 'L'},
615 len = sizeof(perfstr) - strlen(perfstr) - 1; 416 {"secname", required_argument, 0, 'U'},
616 strncat(perfstr, show, len > ptr - show ? ptr - show : len); 417 {"authproto", required_argument, 0, 'a'},
418 {"privproto", required_argument, 0, 'x'},
419 {"authpasswd", required_argument, 0, 'A'},
420 {"privpasswd", required_argument, 0, 'X'},
421 {"next", no_argument, 0, 'n'},
422 {"offset", required_argument, 0, offset_index},
423 {"invert-search", no_argument, 0, invert_search_index},
424 {"perf-oids", no_argument, 0, 'O'},
425 {"ipv4", no_argument, 0, '4'},
426 {"ipv6", no_argument, 0, '6'},
427 {"multiplier", required_argument, 0, 'M'},
428 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
429 {"connection-prefix", required_argument, 0, connection_prefix_index},
430 {"output-format", required_argument, 0, output_format_index},
431 {"rate", no_argument, 0, calculate_rate},
432 {"rate-multiplier", required_argument, 0, rate_multiplier},
433 {0, 0, 0, 0}};
434
435 if (argc < 2) {
436 process_arguments_wrapper result = {
437 .errorcode = ERROR,
438 };
439 return result;
440 }
617 441
618 if (strcmp(type, "") != 0) { 442 // Count number of OIDs here first
619 strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); 443 int option = 0;
620 } 444 size_t oid_counter = 0;
445 while (true) {
446 int option_char = getopt_long(
447 argc, argv,
448 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
621 449
622 if (warning_thresholds) { 450 if (option_char == -1 || option_char == EOF) {
623 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 451 break;
624 if (thlds[i]->warning && thlds[i]->warning->text) 452 }
625 strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1);
626 }
627 453
628 if (critical_thresholds) { 454 switch (option_char) {
629 if (!warning_thresholds) 455 case 'o': {
630 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 456 // we are going to parse this again, so we work on a copy of that string
631 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 457 char *tmp_oids = strdup(optarg);
632 if (thlds[i]->critical && thlds[i]->critical->text) 458 if (tmp_oids == NULL) {
633 strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); 459 die(STATE_UNKNOWN, "strdup failed");
634 } 460 }
635 461
636 strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); 462 for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL;
637 } 463 ptr = strtok(NULL, ", "), oid_counter++) {
638 }
639
640 /* Save state data, as all data collected now */
641 if (calculate_rate) {
642 string_length = 1024;
643 state_string = malloc(string_length);
644 if (state_string == NULL)
645 die(STATE_UNKNOWN, _("Cannot malloc"));
646
647 current_length = 0;
648 for (int i = 0; i < total_oids; i++) {
649 xasprintf(&temp_string, "%.0f", response_value[i]);
650 if (temp_string == NULL)
651 die(STATE_UNKNOWN, _("Cannot asprintf()"));
652 response_length = strlen(temp_string);
653 if (current_length + response_length > string_length) {
654 string_length = current_length + 1024;
655 state_string = realloc(state_string, string_length);
656 if (state_string == NULL)
657 die(STATE_UNKNOWN, _("Cannot realloc()"));
658 } 464 }
659 strcpy(&state_string[current_length], temp_string); 465 break;
660 current_length = current_length + response_length;
661 state_string[current_length] = ':';
662 current_length++;
663 free(temp_string);
664 } 466 }
665 state_string[--current_length] = '\0'; 467 case '?': /* usage */
666 if (verbose > 2) 468 usage5();
667 printf("State string=%s\n", state_string); 469 // fallthrough
470 case 'h': /* help */
471 print_help();
472 exit(STATE_UNKNOWN);
473 case 'V': /* version */
474 print_revision(progname, NP_VERSION);
475 exit(STATE_UNKNOWN);
668 476
669 /* This is not strictly the same as time now, but any subtle variations will cancel out */ 477 default:
670 np_state_write_string(current_time, state_string); 478 continue;
671 if (previous_state == NULL) {
672 /* Or should this be highest state? */
673 die(STATE_OK, _("No previous data to calculate rate - assume okay"));
674 } 479 }
675 } 480 }
676 481
677 printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); 482 /* Check whether at least one OID was given */
678 if (mult_resp) 483 if (oid_counter == 0) {
679 printf("%s", mult_resp); 484 die(STATE_UNKNOWN, _("No OIDs specified\n"));
485 }
680 486
681 return result; 487 // Allocate space for test units
682} 488 check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit));
489 if (tmp == NULL) {
490 die(STATE_UNKNOWN, "Failed to calloc");
491 }
683 492
684/* process command-line arguments */ 493 for (size_t i = 0; i < oid_counter; i++) {
685int process_arguments(int argc, char **argv) { 494 tmp[i] = check_snmp_test_unit_init();
686 static struct option longopts[] = {STD_LONG_OPTS,
687 {"community", required_argument, 0, 'C'},
688 {"oid", required_argument, 0, 'o'},
689 {"object", required_argument, 0, 'o'},
690 {"delimiter", required_argument, 0, 'd'},
691 {"nulloid", required_argument, 0, 'z'},
692 {"output-delimiter", required_argument, 0, 'D'},
693 {"string", required_argument, 0, 's'},
694 {"timeout", required_argument, 0, 't'},
695 {"regex", required_argument, 0, 'r'},
696 {"ereg", required_argument, 0, 'r'},
697 {"eregi", required_argument, 0, 'R'},
698 {"label", required_argument, 0, 'l'},
699 {"units", required_argument, 0, 'u'},
700 {"port", required_argument, 0, 'p'},
701 {"retries", required_argument, 0, 'e'},
702 {"miblist", required_argument, 0, 'm'},
703 {"protocol", required_argument, 0, 'P'},
704 {"context", required_argument, 0, 'N'},
705 {"seclevel", required_argument, 0, 'L'},
706 {"secname", required_argument, 0, 'U'},
707 {"authproto", required_argument, 0, 'a'},
708 {"privproto", required_argument, 0, 'x'},
709 {"authpasswd", required_argument, 0, 'A'},
710 {"privpasswd", required_argument, 0, 'X'},
711 {"next", no_argument, 0, 'n'},
712 {"rate", no_argument, 0, L_CALCULATE_RATE},
713 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
714 {"offset", required_argument, 0, L_OFFSET},
715 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
716 {"perf-oids", no_argument, 0, 'O'},
717 {"ipv4", no_argument, 0, '4'},
718 {"ipv6", no_argument, 0, '6'},
719 {"multiplier", required_argument, 0, 'M'},
720 {"fmtstr", required_argument, 0, 'f'},
721 {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS},
722 {0, 0, 0, 0}};
723
724 if (argc < 2)
725 return ERROR;
726
727 /* reverse compatibility for very old non-POSIX usage forms */
728 for (int c = 1; c < argc; c++) {
729 if (strcmp("-to", argv[c]) == 0)
730 strcpy(argv[c], "-t");
731 if (strcmp("-wv", argv[c]) == 0)
732 strcpy(argv[c], "-w");
733 if (strcmp("-cv", argv[c]) == 0)
734 strcpy(argv[c], "-c");
735 } 495 }
736 496
737 size_t j = 0; 497 check_snmp_config config = check_snmp_config_init();
738 size_t jj = 0; 498 config.snmp_params.test_units = tmp;
499 config.snmp_params.num_of_test_units = oid_counter;
500
501 option = 0;
502 optind = 1; // Reset argument scanner
503 size_t tmp_oid_counter = 0;
504 size_t eval_counter = 0;
505 size_t unitv_counter = 0;
506 size_t labels_counter = 0;
507 unsigned char *authpasswd = NULL;
508 unsigned char *privpasswd = NULL;
509 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
510 char *port = NULL;
511 char *miblist = NULL;
512 char *connection_prefix = NULL;
513 bool snmp_version_set_explicitely = false;
514 // TODO error checking
739 while (true) { 515 while (true) {
740 int option = 0; 516 int option_char = getopt_long(
741 int option_char = getopt_long(argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); 517 argc, argv,
518 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
742 519
743 if (option_char == -1 || option_char == EOF) 520 if (option_char == -1 || option_char == EOF) {
744 break; 521 break;
522 }
745 523
746 switch (option_char) { 524 switch (option_char) {
747 case '?': /* usage */ 525 case '?': /* usage */
@@ -758,64 +536,155 @@ int process_arguments(int argc, char **argv) {
758 536
759 /* Connection info */ 537 /* Connection info */
760 case 'C': /* group or community */ 538 case 'C': /* group or community */
761 community = optarg; 539 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
540 config.snmp_params.snmp_session.community_len = strlen(optarg);
762 break; 541 break;
763 case 'H': /* Host or server */ 542 case 'H': /* Host or server */
764 server_address = optarg; 543 config.snmp_params.snmp_session.peername = optarg;
765 break; 544 break;
766 case 'p': /* TCP port number */ 545 case 'p': /*port number */
546 // Add port to "peername" below to not rely on argument order
767 port = optarg; 547 port = optarg;
768 break; 548 break;
769 case 'm': /* List of MIBS */ 549 case 'm': /* List of MIBS */
770 miblist = optarg; 550 miblist = optarg;
771 break; 551 break;
772 case 'n': /* usesnmpgetnext */ 552 case 'n': /* use_getnext instead of get */
773 usesnmpgetnext = true; 553 config.snmp_params.use_getnext = true;
774 break; 554 break;
775 case 'P': /* SNMP protocol version */ 555 case 'P': /* SNMP protocol version */
776 proto = optarg; 556 if (strcasecmp("1", optarg) == 0) {
557 config.snmp_params.snmp_session.version = SNMP_VERSION_1;
558 } else if (strcasecmp("2c", optarg) == 0) {
559 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
560 } else if (strcasecmp("3", optarg) == 0) {
561 config.snmp_params.snmp_session.version = SNMP_VERSION_3;
562 } else {
563 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg);
564 }
565 snmp_version_set_explicitely = true;
566
777 break; 567 break;
778 case 'N': /* SNMPv3 context */ 568 case 'N': /* SNMPv3 context name */
779 context = optarg; 569 config.snmp_params.snmp_session.contextName = optarg;
570 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
780 break; 571 break;
781 case 'L': /* security level */ 572 case 'L': /* security level */
782 seclevel = optarg; 573 if (strcasecmp("noAuthNoPriv", optarg) == 0) {
574 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
575 } else if (strcasecmp("authNoPriv", optarg) == 0) {
576 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
577 } else if (strcasecmp("authPriv", optarg) == 0) {
578 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
579 } else {
580 die(STATE_UNKNOWN, "invalid security level: %s", optarg);
581 }
783 break; 582 break;
784 case 'U': /* security username */ 583 case 'U': /* security username */
785 secname = optarg; 584 config.snmp_params.snmp_session.securityName = optarg;
585 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
786 break; 586 break;
787 case 'a': /* auth protocol */ 587 case 'a': /* auth protocol */
788 authproto = optarg; 588 // SNMPv3: SHA or MD5
589 // TODO Test for availability of individual protocols
590 if (strcasecmp("MD5", optarg) == 0) {
591 config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol;
592 config.snmp_params.snmp_session.securityAuthProtoLen =
593 OID_LENGTH(usmHMACMD5AuthProtocol);
594 } else if (strcasecmp("SHA", optarg) == 0) {
595 config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol;
596 config.snmp_params.snmp_session.securityAuthProtoLen =
597 OID_LENGTH(usmHMACSHA1AuthProtocol);
598 } else if (strcasecmp("SHA224", optarg) == 0) {
599 config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
600 config.snmp_params.snmp_session.securityAuthProtoLen =
601 OID_LENGTH(usmHMAC128SHA224AuthProtocol);
602 } else if (strcasecmp("SHA256", optarg) == 0) {
603 config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
604 config.snmp_params.snmp_session.securityAuthProtoLen =
605 OID_LENGTH(usmHMAC192SHA256AuthProtocol);
606 } else if (strcasecmp("SHA384", optarg) == 0) {
607 config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
608 config.snmp_params.snmp_session.securityAuthProtoLen =
609 OID_LENGTH(usmHMAC256SHA384AuthProtocol);
610 } else if (strcasecmp("SHA512", optarg) == 0) {
611 config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
612 config.snmp_params.snmp_session.securityAuthProtoLen =
613 OID_LENGTH(usmHMAC384SHA512AuthProtocol);
614 } else {
615 die(STATE_UNKNOWN, "Unknown authentication protocol");
616 }
789 break; 617 break;
790 case 'x': /* priv protocol */ 618 case 'x': /* priv protocol */
791 privproto = optarg; 619 if (strcasecmp("DES", optarg) == 0) {
620#ifdef HAVE_USM_DES_PRIV_PROTOCOL
621 config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol;
622 config.snmp_params.snmp_session.securityAuthProtoLen =
623 OID_LENGTH(usmDESPrivProtocol);
624#else
625 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform");
626#endif
627 } else if (strcasecmp("AES", optarg) == 0) {
628 config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol;
629 config.snmp_params.snmp_session.securityAuthProtoLen =
630 OID_LENGTH(usmAESPrivProtocol);
631 // } else if (strcasecmp("AES128", optarg)) {
632 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol;
633 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol)
634 // / OID_LENGTH(oid);
635 } else if (strcasecmp("AES192", optarg) == 0) {
636 config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol;
637 config.snmp_params.snmp_session.securityAuthProtoLen =
638 OID_LENGTH(usmAES192PrivProtocol);
639 } else if (strcasecmp("AES256", optarg) == 0) {
640 config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol;
641 config.snmp_params.snmp_session.securityAuthProtoLen =
642 OID_LENGTH(usmAES256PrivProtocol);
643 // } else if (strcasecmp("AES192Cisco", optarg)) {
644 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol;
645 // config.snmp_session.securityAuthProtoLen =
646 // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if
647 // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto =
648 // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen =
649 // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if
650 // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto
651 // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
652 // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if
653 // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto
654 // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
655 // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid);
656 } else {
657 die(STATE_UNKNOWN, "Unknown privacy protocol");
658 }
792 break; 659 break;
793 case 'A': /* auth passwd */ 660 case 'A': /* auth passwd */
794 authpasswd = optarg; 661 authpasswd = (unsigned char *)optarg;
795 break; 662 break;
796 case 'X': /* priv passwd */ 663 case 'X': /* priv passwd */
797 privpasswd = optarg; 664 privpasswd = (unsigned char *)optarg;
665 break;
666 case 'e':
667 case 'E':
668 if (!is_integer(optarg)) {
669 usage2(_("Retries interval must be a positive integer"), optarg);
670 } else {
671 config.snmp_params.snmp_session.retries = atoi(optarg);
672 }
798 break; 673 break;
799 case 't': /* timeout period */ 674 case 't': /* timeout period */
800 if (!is_integer(optarg)) 675 if (!is_integer(optarg)) {
801 usage2(_("Timeout interval must be a positive integer"), optarg); 676 usage2(_("Timeout interval must be a positive integer"), optarg);
802 else 677 } else {
803 timeout_interval = atoi(optarg); 678 timeout_interval = (unsigned int)atoi(optarg);
679 }
804 break; 680 break;
805 681
806 /* Test parameters */ 682 /* Test parameters */
807 case 'c': /* critical threshold */ 683 case 'c': /* critical threshold */
808 critical_thresholds = optarg; 684 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
809 break; 685 break;
810 case 'w': /* warning threshold */ 686 case 'w': /* warning threshold */
811 warning_thresholds = optarg; 687 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
812 break;
813 case 'e': /* PRELIMINARY - may change */
814 case 'E': /* PRELIMINARY - may change */
815 if (!is_integer(optarg))
816 usage2(_("Retries interval must be a positive integer"), optarg);
817 else
818 retries = atoi(optarg);
819 break; 688 break;
820 case 'o': /* object identifier */ 689 case 'o': /* object identifier */
821 if (strspn(optarg, "0123456789.,") != strlen(optarg)) { 690 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
@@ -824,306 +693,292 @@ int process_arguments(int argc, char **argv) {
824 * so we have a mib variable, rather than just an SNMP OID, 693 * so we have a mib variable, rather than just an SNMP OID,
825 * so we have to actually read the mib files 694 * so we have to actually read the mib files
826 */ 695 */
827 needmibs = true; 696 config.snmp_params.need_mibs = true;
828 }
829 for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) {
830 while (j >= oids_size) {
831 oids_size += OID_COUNT_STEP;
832 oids = realloc(oids, oids_size * sizeof(*oids));
833 }
834 oids[j] = strdup(ptr);
835 } 697 }
836 numoids = j; 698
837 if (option_char == 'E' || option_char == 'e') { 699 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
838 jj++; 700 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
839 while (j + 1 >= eval_size) { 701 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
840 eval_size += OID_COUNT_STEP;
841 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
842 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
843 }
844 if (option_char == 'E')
845 eval_method[j + 1] |= WARN_PRESENT;
846 else if (option_char == 'e')
847 eval_method[j + 1] |= CRIT_PRESENT;
848 } 702 }
849 break; 703 break;
850 case 'z': /* Null OID Return Check */ 704 case 'z': /* Null OID Return Check */
851 if (!is_integer(optarg)) 705 if (!is_integer(optarg)) {
852 usage2(_("Exit status must be a positive integer"), optarg); 706 usage2(_("Exit status must be a positive integer"), optarg);
853 else 707 } else {
854 nulloid = atoi(optarg); 708 config.evaluation_params.nulloid_result = atoi(optarg);
709 }
855 break; 710 break;
856 case 's': /* string or substring */ 711 case 's': /* string or substring */
857 strncpy(string_value, optarg, sizeof(string_value) - 1); 712 strncpy(config.evaluation_params.string_cmp_value, optarg,
858 string_value[sizeof(string_value) - 1] = 0; 713 sizeof(config.evaluation_params.string_cmp_value) - 1);
859 while (jj >= eval_size) { 714 config.evaluation_params
860 eval_size += OID_COUNT_STEP; 715 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
861 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 716 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
862 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
863 }
864 eval_method[jj++] = CRIT_STRING;
865 break; 717 break;
866 case 'R': /* regex */ 718 case 'R': /* regex */
867 cflags = REG_ICASE; 719 cflags = REG_ICASE;
868 // fall through 720 // fall through
869 case 'r': /* regex */ 721 case 'r': /* regex */
722 {
723 char regex_expect[MAX_INPUT_BUFFER] = "";
870 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 724 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
871 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); 725 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
872 regex_expect[sizeof(regex_expect) - 1] = 0; 726 regex_expect[sizeof(regex_expect) - 1] = 0;
873 errcode = regcomp(&preg, regex_expect, cflags); 727 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
874 if (errcode != 0) { 728 if (errcode != 0) {
875 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 729 char errbuf[MAX_INPUT_BUFFER] = "";
876 printf(_("Could Not Compile Regular Expression")); 730 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
877 return ERROR; 731 MAX_INPUT_BUFFER);
878 } 732 printf("Could Not Compile Regular Expression: %s", errbuf);
879 while (jj >= eval_size) { 733 process_arguments_wrapper result = {
880 eval_size += OID_COUNT_STEP; 734 .errorcode = ERROR,
881 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 735 };
882 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); 736 return result;
883 } 737 }
884 eval_method[jj++] = CRIT_REGEX; 738 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
885 break; 739 } break;
886
887 /* Format */
888 case 'd': /* delimiter */
889 delimiter = strscpy(delimiter, optarg);
890 break;
891 case 'D': /* output-delimiter */
892 output_delim = strscpy(output_delim, optarg);
893 break;
894 case 'l': /* label */ 740 case 'l': /* label */
895 nlabels++; 741 {
896 if (nlabels > labels_size) { 742 if (labels_counter >= config.snmp_params.num_of_test_units) {
897 labels_size += 8; 743 break;
898 labels = realloc(labels, labels_size * sizeof(*labels)); 744 }
899 if (labels == NULL) 745 char *ptr = trim_whitespaces_and_check_quoting(optarg);
900 die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); 746 if (ptr[0] == '\'') {
747 config.snmp_params.test_units[labels_counter].label = ptr + 1;
748 } else {
749 config.snmp_params.test_units[labels_counter].label = ptr;
901 } 750 }
902 labels[nlabels - 1] = optarg; 751
903 char *ptr = thisarg(optarg); 752 while (ptr && (ptr = get_next_argument(ptr))) {
904 labels[nlabels - 1] = ptr; 753 labels_counter++;
905 if (ptr[0] == '\'') 754 ptr = trim_whitespaces_and_check_quoting(ptr);
906 labels[nlabels - 1] = ptr + 1; 755 if (ptr[0] == '\'') {
907 while (ptr && (ptr = nextarg(ptr))) { 756 config.snmp_params.test_units[labels_counter].label = ptr + 1;
908 nlabels++; 757 } else {
909 if (nlabels > labels_size) { 758 config.snmp_params.test_units[labels_counter].label = ptr;
910 labels_size += 8;
911 labels = realloc(labels, labels_size * sizeof(*labels));
912 if (labels == NULL)
913 die(STATE_UNKNOWN, _("Could not reallocate labels\n"));
914 } 759 }
915 ptr = thisarg(ptr);
916 if (ptr[0] == '\'')
917 labels[nlabels - 1] = ptr + 1;
918 else
919 labels[nlabels - 1] = ptr;
920 } 760 }
921 break; 761 labels_counter++;
762 } break;
922 case 'u': /* units */ 763 case 'u': /* units */
923 units = optarg; 764 {
924 nunits++; 765 if (unitv_counter >= config.snmp_params.num_of_test_units) {
925 if (nunits > unitv_size) { 766 break;
926 unitv_size += 8;
927 unitv = realloc(unitv, unitv_size * sizeof(*unitv));
928 if (unitv == NULL)
929 die(STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
930 } 767 }
931 unitv[nunits - 1] = optarg; 768 char *ptr = trim_whitespaces_and_check_quoting(optarg);
932 ptr = thisarg(optarg); 769 if (ptr[0] == '\'') {
933 unitv[nunits - 1] = ptr; 770 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
934 if (ptr[0] == '\'') 771 } else {
935 unitv[nunits - 1] = ptr + 1; 772 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
936 while (ptr && (ptr = nextarg(ptr))) { 773 }
937 if (nunits > unitv_size) { 774 while (ptr && (ptr = get_next_argument(ptr))) {
938 unitv_size += 8; 775 unitv_counter++;
939 unitv = realloc(unitv, unitv_size * sizeof(*unitv)); 776 ptr = trim_whitespaces_and_check_quoting(ptr);
940 if (units == NULL) 777 if (ptr[0] == '\'') {
941 die(STATE_UNKNOWN, _("Could not realloc() units\n")); 778 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
779 } else {
780 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
942 } 781 }
943 nunits++;
944 ptr = thisarg(ptr);
945 if (ptr[0] == '\'')
946 unitv[nunits - 1] = ptr + 1;
947 else
948 unitv[nunits - 1] = ptr;
949 } 782 }
783 unitv_counter++;
784 } break;
785 case offset_index:
786 config.evaluation_params.offset = strtod(optarg, NULL);
787 config.evaluation_params.offset_set = true;
950 break; 788 break;
951 case L_CALCULATE_RATE: 789 case invert_search_index:
952 if (calculate_rate == 0) 790 config.evaluation_params.invert_search = false;
953 np_enable_state(NULL, 1);
954 calculate_rate = 1;
955 break;
956 case L_RATE_MULTIPLIER:
957 if (!is_integer(optarg) || ((rate_multiplier = atoi(optarg)) <= 0))
958 usage2(_("Rate multiplier must be a positive integer"), optarg);
959 break;
960 case L_OFFSET:
961 offset = strtod(optarg, NULL);
962 break;
963 case L_INVERT_SEARCH:
964 invert_search = 1;
965 break; 791 break;
966 case 'O': 792 case 'O':
967 perf_labels = 0; 793 config.evaluation_params.use_oid_as_perf_data_label = true;
968 break; 794 break;
969 case '4': 795 case '4':
796 // The default, do something here to be exclusive to -6 instead of doing nothing?
797 connection_prefix = "udp";
970 break; 798 break;
971 case '6': 799 case '6':
972 xasprintf(&ip_version, "udp6:"); 800 connection_prefix = "udp6";
973 if (verbose > 2) 801 break;
974 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); 802 case connection_prefix_index:
803 connection_prefix = optarg;
975 break; 804 break;
976 case 'M': 805 case 'M':
977 if (strspn(optarg, "0123456789.,") == strlen(optarg)) { 806 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
978 multiplier = strtod(optarg, NULL); 807 config.evaluation_params.multiplier = strtod(optarg, NULL);
808 config.evaluation_params.multiplier_set = true;
979 } 809 }
980 break; 810 break;
981 case 'f': 811 case ignore_mib_parsing_errors_index:
982 if (multiplier != 1.0) { 812 config.snmp_params.ignore_mib_parsing_errors = true;
983 fmtstr = optarg; 813 break;
984 fmtstr_set = true; 814 case 'f': // Deprecated format option for floating point values
815 break;
816 case output_format_index: {
817 parsed_output_format parser = mp_parse_output_format(optarg);
818 if (!parser.parsing_success) {
819 // TODO List all available formats here, maybe add anothoer usage function
820 printf("Invalid output format: %s\n", optarg);
821 exit(STATE_UNKNOWN);
822 }
823
824 config.output_format_is_set = true;
825 config.output_format = parser.output_format;
826 break;
827 }
828 case calculate_rate:
829 config.evaluation_params.calculate_rate = true;
830 break;
831 case rate_multiplier:
832 if (!is_integer(optarg) ||
833 ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) {
834 usage2(_("Rate multiplier must be a positive integer"), optarg);
985 } 835 }
986 break; 836 break;
987 case L_IGNORE_MIB_PARSING_ERRORS: 837 default:
988 ignore_mib_parsing_errors = true; 838 die(STATE_UNKNOWN, "Unknown option");
989 } 839 }
990 } 840 }
991 841
992 if (server_address == NULL) 842 if (config.snmp_params.snmp_session.peername == NULL) {
993 server_address = argv[optind]; 843 config.snmp_params.snmp_session.peername = argv[optind];
994 844 }
995 if (community == NULL)
996 community = strdup(DEFAULT_COMMUNITY);
997
998 return validate_arguments();
999}
1000
1001/******************************************************************************
1002
1003@@-
1004<sect3>
1005<title>validate_arguments</title>
1006
1007<para>&PROTO_validate_arguments;</para>
1008
1009<para>Checks to see if the default miblist needs to be loaded. Also verifies
1010the authentication and authorization combinations based on protocol version
1011selected.</para>
1012
1013<para></para>
1014
1015</sect3>
1016-@@
1017******************************************************************************/
1018 845
1019static int validate_arguments() { 846 // Build true peername here if necessary
1020 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 847 if (connection_prefix != NULL) {
1021 if (miblist == NULL) { 848 // We got something in the connection prefix
1022 if (needmibs) { 849 if (strcasecmp(connection_prefix, "udp") == 0) {
1023 miblist = strdup(DEFAULT_MIBLIST); 850 // The default, do nothing
851 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
852 // use tcp/ipv4
853 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
854 config.snmp_params.snmp_session.peername);
855 } else if (strcasecmp(connection_prefix, "tcp6") == 0 ||
856 strcasecmp(connection_prefix, "tcpv6") == 0 ||
857 strcasecmp(connection_prefix, "tcpipv6") == 0 ||
858 strcasecmp(connection_prefix, "udp6") == 0 ||
859 strcasecmp(connection_prefix, "udpipv6") == 0 ||
860 strcasecmp(connection_prefix, "udpv6") == 0) {
861 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it
862 // works anyway therefore do nothing here
863 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix,
864 config.snmp_params.snmp_session.peername);
865 } else if (strcmp(connection_prefix, "tls") == 0) {
866 // TODO: Anything else to do here?
867 xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s",
868 config.snmp_params.snmp_session.peername);
869 } else if (strcmp(connection_prefix, "dtls") == 0) {
870 // TODO: Anything else to do here?
871 xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s",
872 config.snmp_params.snmp_session.peername);
873 } else if (strcmp(connection_prefix, "unix") == 0) {
874 // TODO: Check whether this is a valid path?
875 xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s",
876 config.snmp_params.snmp_session.peername);
877 } else if (strcmp(connection_prefix, "ipx") == 0) {
878 xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s",
879 config.snmp_params.snmp_session.peername);
1024 } else { 880 } else {
1025 miblist = ""; /* don't read any mib files for numeric oids */ 881 // Don't know that prefix, die here
882 die(STATE_UNKNOWN, "Unknown connection prefix");
1026 } 883 }
1027 } 884 }
1028 885
1029 /* Check server_address is given */ 886 /* Check server_address is given */
1030 if (server_address == NULL) 887 if (config.snmp_params.snmp_session.peername == NULL) {
1031 die(STATE_UNKNOWN, _("No host specified\n")); 888 die(STATE_UNKNOWN, _("No host specified\n"));
889 }
1032 890
1033 /* Check oid is given */ 891 if (port != NULL) {
1034 if (numoids == 0) 892 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
1035 die(STATE_UNKNOWN, _("No OIDs specified\n")); 893 config.snmp_params.snmp_session.peername, port);
894 }
1036 895
1037 if (proto == NULL) 896 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1038 xasprintf(&proto, DEFAULT_PROTOCOL); 897 if (miblist == NULL) {
1039 898 if (config.snmp_params.need_mibs) {
1040 if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ 899 setenv("MIBLS", DEFAULT_MIBLIST, 1);
1041 numauthpriv = 2; 900 } else {
1042 authpriv = calloc(numauthpriv, sizeof(char *)); 901 setenv("MIBLS", "NONE", 1);
1043 authpriv[0] = strdup("-c"); 902 miblist = ""; /* don't read any mib files for numeric oids */
1044 authpriv[1] = strdup(community);
1045 } else if (strcmp(proto, "3") == 0) { /* snmpv3 args */
1046 if (!(context == NULL)) {
1047 numcontext = 2;
1048 contextargs = calloc(numcontext, sizeof(char *));
1049 contextargs[0] = strdup("-n");
1050 contextargs[1] = strdup(context);
1051 } 903 }
904 } else {
905 // Blatantly stolen from snmplib/snmp_parse_args
906 setenv("MIBS", miblist, 1);
907 }
1052 908
1053 if (seclevel == NULL) 909 // Historical default is SNMP v2c
1054 xasprintf(&seclevel, "noAuthNoPriv"); 910 if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) {
911 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
912 }
1055 913
1056 if (secname == NULL) 914 if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) ||
915 (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */
916 /*
917 config.numauthpriv = 2;
918 config.authpriv = calloc(config.numauthpriv, sizeof(char *));
919 config.authpriv[0] = strdup("-c");
920 config.authpriv[1] = strdup(community);
921 */
922 } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */
923 // generate keys for priv and auth here (if demanded)
924
925 if (config.snmp_params.snmp_session.securityName == NULL) {
1057 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 926 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
927 }
1058 928
1059 if (strcmp(seclevel, "noAuthNoPriv") == 0) { 929 switch (config.snmp_params.snmp_session.securityLevel) {
1060 numauthpriv = 4; 930 case SNMP_SEC_LEVEL_AUTHPRIV: {
1061 authpriv = calloc(numauthpriv, sizeof(char *)); 931 if (authpasswd == NULL) {
1062 authpriv[0] = strdup("-l"); 932 die(STATE_UNKNOWN,
1063 authpriv[1] = strdup("noAuthNoPriv"); 933 "No authentication passphrase was given, but authorization was requested");
1064 authpriv[2] = strdup("-u");
1065 authpriv[3] = strdup(secname);
1066 } else {
1067 if (!((strcmp(seclevel, "authNoPriv") == 0) || (strcmp(seclevel, "authPriv") == 0))) {
1068 usage2(_("Invalid seclevel"), seclevel);
1069 } 934 }
1070 935 // auth and priv
1071 if (authproto == NULL) 936 int priv_key_generated = generate_Ku(
1072 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); 937 config.snmp_params.snmp_session.securityPrivProto,
1073 938 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
1074 if (authpasswd == NULL) 939 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
1075 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); 940 &config.snmp_params.snmp_session.securityPrivKeyLen);
1076 941
1077 if (strcmp(seclevel, "authNoPriv") == 0) { 942 if (priv_key_generated != SNMPERR_SUCCESS) {
1078 numauthpriv = 8; 943 die(STATE_UNKNOWN, "Failed to generate privacy key");
1079 authpriv = calloc(numauthpriv, sizeof(char *));
1080 authpriv[0] = strdup("-l");
1081 authpriv[1] = strdup("authNoPriv");
1082 authpriv[2] = strdup("-a");
1083 authpriv[3] = strdup(authproto);
1084 authpriv[4] = strdup("-u");
1085 authpriv[5] = strdup(secname);
1086 authpriv[6] = strdup("-A");
1087 authpriv[7] = strdup(authpasswd);
1088 } else if (strcmp(seclevel, "authPriv") == 0) {
1089 if (privproto == NULL)
1090 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1091
1092 if (privpasswd == NULL)
1093 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1094
1095 numauthpriv = 12;
1096 authpriv = calloc(numauthpriv, sizeof(char *));
1097 authpriv[0] = strdup("-l");
1098 authpriv[1] = strdup("authPriv");
1099 authpriv[2] = strdup("-a");
1100 authpriv[3] = strdup(authproto);
1101 authpriv[4] = strdup("-u");
1102 authpriv[5] = strdup(secname);
1103 authpriv[6] = strdup("-A");
1104 authpriv[7] = strdup(authpasswd);
1105 authpriv[8] = strdup("-x");
1106 authpriv[9] = strdup(privproto);
1107 authpriv[10] = strdup("-X");
1108 authpriv[11] = strdup(privpasswd);
1109 } 944 }
1110 } 945 }
1111 946 // fall through
1112 } else { 947 case SNMP_SEC_LEVEL_AUTHNOPRIV: {
1113 usage2(_("Invalid SNMP version"), proto); 948 if (privpasswd == NULL) {
949 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested");
950 }
951 int auth_key_generated = generate_Ku(
952 config.snmp_params.snmp_session.securityAuthProto,
953 (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd,
954 strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey,
955 &config.snmp_params.snmp_session.securityAuthKeyLen);
956
957 if (auth_key_generated != SNMPERR_SUCCESS) {
958 die(STATE_UNKNOWN, "Failed to generate privacy key");
959 }
960 } break;
961 case SNMP_SEC_LEVEL_NOAUTH:
962 // No auth, no priv, not much todo
963 break;
964 }
1114 } 965 }
1115 966
1116 return OK; 967 process_arguments_wrapper result = {
968 .config = config,
969 .errorcode = OK,
970 };
971 return result;
1117} 972}
1118 973
1119/* trim leading whitespace 974/* trim leading whitespace
1120 if there is a leading quote, make sure it balances */ 975 if there is a leading quote, make sure it balances */
1121 976char *trim_whitespaces_and_check_quoting(char *str) {
1122static char *thisarg(char *str) {
1123 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ 977 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
1124 if (str[0] == '\'') { /* handle SIMPLE quoted strings */ 978 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1125 if (strlen(str) == 1 || !strstr(str + 1, "'")) 979 if (strlen(str) == 1 || !strstr(str + 1, "'")) {
1126 die(STATE_UNKNOWN, _("Unbalanced quotes\n")); 980 die(STATE_UNKNOWN, _("Unbalanced quotes\n"));
981 }
1127 } 982 }
1128 return str; 983 return str;
1129} 984}
@@ -1132,23 +987,21 @@ static char *thisarg(char *str) {
1132 set the trailing quote to '\x0' 987 set the trailing quote to '\x0'
1133 if the string continues, advance beyond the comma */ 988 if the string continues, advance beyond the comma */
1134 989
1135static char *nextarg(char *str) { 990char *get_next_argument(char *str) {
1136 if (str[0] == '\'') { 991 if (str[0] == '\'') {
1137 str[0] = 0; 992 str[0] = 0;
1138 if (strlen(str) > 1) { 993 if (strlen(str) > 1) {
1139 str = strstr(str + 1, "'"); 994 str = strstr(str + 1, "'");
1140 return (++str); 995 return (++str);
1141 } else {
1142 return NULL;
1143 } 996 }
997 return NULL;
1144 } 998 }
1145 if (str[0] == ',') { 999 if (str[0] == ',') {
1146 str[0] = 0; 1000 str[0] = 0;
1147 if (strlen(str) > 1) { 1001 if (strlen(str) > 1) {
1148 return (++str); 1002 return (++str);
1149 } else {
1150 return NULL;
1151 } 1003 }
1004 return NULL;
1152 } 1005 }
1153 if ((str = strstr(str, ",")) && strlen(str) > 1) { 1006 if ((str = strstr(str, ",")) && strlen(str) > 1) {
1154 str[0] = 0; 1007 str[0] = 0;
@@ -1157,41 +1010,7 @@ static char *nextarg(char *str) {
1157 return NULL; 1010 return NULL;
1158} 1011}
1159 1012
1160/* multiply result (values 0 < n < 1 work as divider) */ 1013void print_help(void) {
1161static char *multiply(char *str) {
1162 if (multiplier == 1)
1163 return (str);
1164
1165 if (verbose > 2)
1166 printf(" multiply input: %s\n", str);
1167
1168 char *endptr;
1169 double val = strtod(str, &endptr);
1170 if ((val == 0.0) && (endptr == str)) {
1171 die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str);
1172 }
1173
1174 if (verbose > 2)
1175 printf(" multiply extracted double: %f\n", val);
1176
1177 val *= multiplier;
1178 char *conv = "%f";
1179 if (fmtstr_set) {
1180 conv = fmtstr;
1181 }
1182 if (val == (int)val) {
1183 snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val);
1184 } else {
1185 if (verbose > 2)
1186 printf(" multiply using format: %s\n", conv);
1187 snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val);
1188 }
1189 if (verbose > 2)
1190 printf(" multiply result: %s\n", buffer);
1191 return buffer;
1192}
1193
1194static void print_help(void) {
1195 print_revision(progname, NP_VERSION); 1014 print_revision(progname, NP_VERSION);
1196 1015
1197 printf(COPYRIGHT, copyright, email); 1016 printf(COPYRIGHT, copyright, email);
@@ -1204,8 +1023,6 @@ static void print_help(void) {
1204 1023
1205 printf(UT_HELP_VRSN); 1024 printf(UT_HELP_VRSN);
1206 printf(UT_EXTRA_OPTS); 1025 printf(UT_EXTRA_OPTS);
1207 printf(UT_IPv46);
1208
1209 printf(UT_HOST_PORT, 'p', DEFAULT_PORT); 1026 printf(UT_HOST_PORT, 'p', DEFAULT_PORT);
1210 1027
1211 /* SNMP and Authentication Protocol */ 1028 /* SNMP and Authentication Protocol */
@@ -1217,13 +1034,15 @@ static void print_help(void) {
1217 printf(" %s\n", _("SNMPv3 context")); 1034 printf(" %s\n", _("SNMPv3 context"));
1218 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); 1035 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1219 printf(" %s\n", _("SNMPv3 securityLevel")); 1036 printf(" %s\n", _("SNMPv3 securityLevel"));
1220 printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); 1037 printf(" %s\n", "-a, --authproto=[MD5|SHA]");
1221 printf(" %s\n", 1038 printf(" %s\n", _("SNMPv3 auth proto"));
1222 _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); 1039#ifdef HAVE_USM_DES_PRIV_PROTOCOL
1223 printf(" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); 1040 printf(" %s\n", "-x, --privproto=[DES|AES]");
1224 printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); 1041 printf(" %s\n", _("SNMPv3 priv proto (default DES)"));
1225 printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); 1042#else
1226 printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); 1043 printf(" %s\n", "-x, --privproto=[AES]");
1044 printf(" %s\n", _("SNMPv3 priv proto (default AES)"));
1045#endif
1227 1046
1228 /* Authentication Tokens*/ 1047 /* Authentication Tokens*/
1229 printf(" %s\n", "-C, --community=STRING"); 1048 printf(" %s\n", "-C, --community=STRING");
@@ -1235,15 +1054,18 @@ static void print_help(void) {
1235 printf(" %s\n", _("SNMPv3 authentication password")); 1054 printf(" %s\n", _("SNMPv3 authentication password"));
1236 printf(" %s\n", "-X, --privpasswd=PASSWORD"); 1055 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1237 printf(" %s\n", _("SNMPv3 privacy password")); 1056 printf(" %s\n", _("SNMPv3 privacy password"));
1057 printf(" %s\n", "--connection-prefix");
1058 printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, "
1059 "tcp6, tcpv6, tcpipv6, tls, dtls - "
1060 "default is \"udp\"\n");
1238 1061
1239 /* OID Stuff */ 1062 /* OID Stuff */
1240 printf(" %s\n", "-o, --oid=OID(s)"); 1063 printf(" %s\n", "-o, --oid=OID(s)");
1241 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); 1064 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1242 printf(" %s\n", "-m, --miblist=STRING"); 1065 printf(" %s\n", "-m, --miblist=STRING");
1243 printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); 1066 printf(" %s\n",
1067 _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1244 printf(" %s\n", _("for symbolic OIDs.)")); 1068 printf(" %s\n", _("for symbolic OIDs.)"));
1245 printf(" %s\n", "-d, --delimiter=STRING");
1246 printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1247 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); 1069 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1248 printf(" %s\n", _("to be the data that should be used in the evaluation.")); 1070 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1249 printf(" %s\n", "-z, --nulloid=#"); 1071 printf(" %s\n", "-z, --nulloid=#");
@@ -1260,10 +1082,6 @@ static void print_help(void) {
1260 printf(" %s\n", _("Warning threshold range(s)")); 1082 printf(" %s\n", _("Warning threshold range(s)"));
1261 printf(" %s\n", "-c, --critical=THRESHOLD(s)"); 1083 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1262 printf(" %s\n", _("Critical threshold range(s)")); 1084 printf(" %s\n", _("Critical threshold range(s)"));
1263 printf(" %s\n", "--rate");
1264 printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1265 printf(" %s\n", "--rate-multiplier");
1266 printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1267 printf(" %s\n", "--offset=OFFSET"); 1085 printf(" %s\n", "--offset=OFFSET");
1268 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); 1086 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1269 1087
@@ -1271,9 +1089,11 @@ static void print_help(void) {
1271 printf(" %s\n", "-s, --string=STRING"); 1089 printf(" %s\n", "-s, --string=STRING");
1272 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); 1090 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1273 printf(" %s\n", "-r, --ereg=REGEX"); 1091 printf(" %s\n", "-r, --ereg=REGEX");
1274 printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); 1092 printf(" %s\n",
1093 _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1275 printf(" %s\n", "-R, --eregi=REGEX"); 1094 printf(" %s\n", "-R, --eregi=REGEX");
1276 printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); 1095 printf(" %s\n",
1096 _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1277 printf(" %s\n", "--invert-search"); 1097 printf(" %s\n", "--invert-search");
1278 printf(" %s\n", _("Invert search result (CRITICAL if found)")); 1098 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1279 1099
@@ -1282,53 +1102,46 @@ static void print_help(void) {
1282 printf(" %s\n", _("Prefix label for output from plugin")); 1102 printf(" %s\n", _("Prefix label for output from plugin"));
1283 printf(" %s\n", "-u, --units=STRING"); 1103 printf(" %s\n", "-u, --units=STRING");
1284 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); 1104 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1285 printf(" %s\n", "-D, --output-delimiter=STRING");
1286 printf(" %s\n", _("Separates output on multiple OID requests"));
1287 printf(" %s\n", "-M, --multiplier=FLOAT"); 1105 printf(" %s\n", "-M, --multiplier=FLOAT");
1288 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); 1106 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1289 printf(" %s\n", "-f, --fmtstr=STRING"); 1107 printf(UT_OUTPUT_FORMAT);
1290 printf(" %s\n", _("C-style format string for float values (see option -M)"));
1291 1108
1292 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1109 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1293 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5")); 1110 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: "
1111 "timeout_interval * retries + 5"));
1294 printf(" %s\n", "-e, --retries=INTEGER"); 1112 printf(" %s\n", "-e, --retries=INTEGER");
1295 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); 1113 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "),
1114 DEFAULT_RETRIES);
1296 1115
1297 printf(" %s\n", "-O, --perf-oids"); 1116 printf(" %s\n", "-O, --perf-oids");
1298 printf(" %s\n", _("Label performance data with OIDs instead of --label's")); 1117 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1299 1118
1300 printf(" %s\n", "--ignore-mib-parsing-errors"); 1119 printf(" %s\n", "--ignore-mib-parsing-errors");
1301 printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); 1120 printf(" %s\n", _("Do to not print errors encountered when parsing MIB files"));
1302 1121
1303 printf(UT_VERBOSE); 1122 printf(UT_VERBOSE);
1304 1123
1305 printf("\n"); 1124 printf("\n");
1306 printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); 1125 printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries."));
1307 printf("%s\n", _("if you don't have the package installed, you will need to download it from")); 1126 printf("%s\n",
1127 _("if you don't have the libraries installed, you will need to download them from"));
1308 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); 1128 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1309 1129
1310 printf("\n"); 1130 printf("\n");
1311 printf("%s\n", _("Notes:")); 1131 printf("%s\n", _("Notes:"));
1312 printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); 1132 printf(" %s\n",
1133 _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1313 printf(" %s\n", _("list (lists with internal spaces must be quoted).")); 1134 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1314 1135
1315 printf(" -%s", UT_THRESHOLDS_NOTES); 1136 printf(" -%s", UT_THRESHOLDS_NOTES);
1316 1137
1317 printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); 1138 printf(" %s\n",
1139 _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1318 printf(" %s\n", _("- Note that only one string and one regex may be checked at present")); 1140 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1319 printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); 1141 printf(" %s\n",
1142 _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1320 printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); 1143 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1321 1144
1322 printf("\n");
1323 printf("%s\n", _("Rate Calculation:"));
1324 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1325 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1326 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1327 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1328 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1329 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1330 printf(" %s\n", _("changing the arguments will create a new state file."));
1331
1332 printf(UT_SUPPORT); 1145 printf(UT_SUPPORT);
1333} 1146}
1334 1147
@@ -1339,5 +1152,5 @@ void print_usage(void) {
1339 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); 1152 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1340 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); 1153 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1341 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); 1154 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1342 printf("[-M multiplier [-f format]]\n"); 1155 printf("[-M multiplier]\n");
1343} 1156}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c
new file mode 100644
index 00000000..ecbfc5dd
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.c
@@ -0,0 +1,934 @@
1#include "./check_snmp_helpers.h"
2#include <string.h>
3#include "../../lib/utils_base.h"
4#include "config.h"
5#include <assert.h>
6#include "../utils.h"
7#include "output.h"
8#include "states.h"
9#include <sys/stat.h>
10#include <ctype.h>
11
12extern int verbose;
13
14check_snmp_test_unit check_snmp_test_unit_init() {
15 check_snmp_test_unit tmp = {
16 .threshold = mp_thresholds_init(),
17 };
18 return tmp;
19}
20
21int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit test_units[],
22 size_t max_test_units, bool is_critical) {
23
24 if (threshold_string == NULL || strlen(threshold_string) == 0) {
25 // No input, do nothing
26 return 0;
27 }
28
29 if (strchr(threshold_string, ',') != NULL) {
30 // Got a comma in the string, should be multiple values
31 size_t tu_index = 0;
32
33 while (threshold_string[0] == ',') {
34 // got commas at the beginning, so skip some values
35 tu_index++;
36 threshold_string++;
37 }
38
39 for (char *ptr = strtok(threshold_string, ", "); ptr != NULL;
40 ptr = strtok(NULL, ", "), tu_index++) {
41
42 if (tu_index > max_test_units) {
43 // More thresholds then values, just ignore them
44 return 0;
45 }
46
47 // edge case: maybe we got `,,` to skip a value
48 if (strlen(ptr) == 0) {
49 // no threshold given, do not set it then
50 continue;
51 }
52
53 mp_range_parsed tmp = mp_parse_range_string(ptr);
54 if (tmp.error != MP_PARSING_SUCCES) {
55 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr);
56 }
57
58 if (is_critical) {
59 test_units[tu_index].threshold.critical = tmp.range;
60 test_units[tu_index].threshold.critical_is_set = true;
61 } else {
62 test_units[tu_index].threshold.warning = tmp.range;
63 test_units[tu_index].threshold.warning_is_set = true;
64 }
65 }
66
67 } else {
68 // Single value
69 // only valid for the first test unit
70 mp_range_parsed tmp = mp_parse_range_string(threshold_string);
71 if (tmp.error != MP_PARSING_SUCCES) {
72 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string);
73 }
74
75 if (is_critical) {
76 test_units[0].threshold.critical = tmp.range;
77 test_units[0].threshold.critical_is_set = true;
78 } else {
79 test_units[0].threshold.warning = tmp.range;
80 test_units[0].threshold.warning_is_set = true;
81 }
82 }
83
84 return 0;
85}
86
87const int DEFAULT_PROTOCOL = SNMP_VERSION_1;
88const char DEFAULT_OUTPUT_DELIMITER[] = " ";
89
90const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192;
91
92check_snmp_config check_snmp_config_init() {
93 check_snmp_config tmp = {
94 .snmp_params =
95 {
96 .use_getnext = false,
97
98 .ignore_mib_parsing_errors = false,
99 .need_mibs = false,
100
101 .test_units = NULL,
102 .num_of_test_units = 0,
103 },
104
105 .evaluation_params =
106 {
107 .nulloid_result = STATE_UNKNOWN, // state to return if no result for query
108
109 .invert_search = true,
110 .regex_cmp_value = {},
111 .string_cmp_value = "",
112
113 .multiplier = 1.0,
114 .multiplier_set = false,
115 .offset = 0,
116 .offset_set = false,
117
118 .use_oid_as_perf_data_label = false,
119
120 .calculate_rate = false,
121 .rate_multiplier = 1,
122 },
123 };
124
125 snmp_sess_init(&tmp.snmp_params.snmp_session);
126
127 tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES;
128 tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION;
129 tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
130 tmp.snmp_params.snmp_session.community = (unsigned char *)"public";
131 tmp.snmp_params.snmp_session.community_len = strlen("public");
132 return tmp;
133}
134
135snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) {
136 if (parameters.ignore_mib_parsing_errors) {
137 char *opt_toggle_res = snmp_mib_toggle_options("e");
138 if (opt_toggle_res != NULL) {
139 die(STATE_UNKNOWN, "Unable to disable MIB parsing errors");
140 }
141 }
142
143 struct snmp_pdu *pdu = NULL;
144 if (parameters.use_getnext) {
145 pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
146 } else {
147 pdu = snmp_pdu_create(SNMP_MSG_GET);
148 }
149
150 for (size_t i = 0; i < parameters.num_of_test_units; i++) {
151 assert(parameters.test_units[i].oid != NULL);
152 if (verbose > 0) {
153 printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid);
154 }
155
156 oid tmp_OID[MAX_OID_LEN];
157 size_t tmp_OID_len = MAX_OID_LEN;
158 if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) {
159 // success
160 snmp_add_null_var(pdu, tmp_OID, tmp_OID_len);
161 } else {
162 // failed
163 snmp_perror("Parsing failure");
164 die(STATE_UNKNOWN, "Failed to parse OID\n");
165 }
166 }
167
168 const int timeout_safety_tolerance = 5;
169 alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) +
170 timeout_safety_tolerance);
171
172 struct snmp_session *active_session = snmp_open(&parameters.snmp_session);
173 if (active_session == NULL) {
174 int pcliberr = 0;
175 int psnmperr = 0;
176 char *pperrstring = NULL;
177 snmp_error(&parameters.snmp_session, &pcliberr, &psnmperr, &pperrstring);
178 die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring);
179 }
180
181 struct snmp_pdu *response = NULL;
182 int snmp_query_status = snmp_synch_response(active_session, pdu, &response);
183
184 if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) {
185 int pcliberr = 0;
186 int psnmperr = 0;
187 char *pperrstring = NULL;
188 snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring);
189
190 if (psnmperr == SNMPERR_TIMEOUT) {
191 // We exit with critical here for some historical reason
192 die(STATE_CRITICAL, "SNMP query ran into a timeout\n");
193 }
194 die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring);
195 }
196
197 snmp_close(active_session);
198
199 /* disable alarm again */
200 alarm(0);
201
202 snmp_responces result = {
203 .errorcode = OK,
204 .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)),
205 };
206
207 if (result.response_values == NULL) {
208 result.errorcode = ERROR;
209 return result;
210 }
211
212 // We got the the query results, now process them
213 size_t loop_index = 0;
214 for (netsnmp_variable_list *vars = response->variables; vars;
215 vars = vars->next_variable, loop_index++) {
216
217 for (size_t jdx = 0; jdx < vars->name_length; jdx++) {
218 result.response_values[loop_index].oid[jdx] = vars->name[jdx];
219 }
220 result.response_values[loop_index].oid_length = vars->name_length;
221
222 switch (vars->type) {
223 case ASN_OCTET_STR: {
224 result.response_values[loop_index].string_response = strdup((char *)vars->val.string);
225 result.response_values[loop_index].type = vars->type;
226 if (verbose) {
227 printf("Debug: Got a string as response: %s\n", vars->val.string);
228 }
229 }
230 continue;
231 case ASN_OPAQUE:
232 if (verbose) {
233 printf("Debug: Got OPAQUE\n");
234 }
235 break;
236 /* Numerical values */
237 case ASN_COUNTER64: {
238 if (verbose) {
239 printf("Debug: Got counter64\n");
240 }
241 struct counter64 tmp = *(vars->val.counter64);
242 uint64_t counter = (tmp.high << 32) + tmp.low;
243 result.response_values[loop_index].value.uIntVal = counter;
244 result.response_values[loop_index].type = vars->type;
245 } break;
246 case ASN_GAUGE: // same as ASN_UNSIGNED
247 case ASN_TIMETICKS:
248 case ASN_COUNTER:
249 case ASN_UINTEGER: {
250 if (verbose) {
251 printf("Debug: Got a Integer like\n");
252 }
253 result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer);
254 result.response_values[loop_index].type = vars->type;
255 } break;
256 case ASN_INTEGER: {
257 if (verbose) {
258 printf("Debug: Got a Integer\n");
259 }
260 result.response_values[loop_index].value.intVal = *(vars->val.integer);
261 result.response_values[loop_index].type = vars->type;
262 } break;
263 case ASN_FLOAT: {
264 if (verbose) {
265 printf("Debug: Got a float\n");
266 }
267 result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal);
268 result.response_values[loop_index].type = vars->type;
269 } break;
270 case ASN_DOUBLE: {
271 if (verbose) {
272 printf("Debug: Got a double\n");
273 }
274 result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal);
275 result.response_values[loop_index].type = vars->type;
276 } break;
277 case ASN_IPADDRESS:
278 if (verbose) {
279 printf("Debug: Got an IP address\n");
280 }
281 result.response_values[loop_index].type = vars->type;
282
283 // TODO: print address here, state always ok? or regex match?
284 break;
285 default:
286 if (verbose) {
287 printf("Debug: Got a unmatched result type: %hhu\n", vars->type);
288 }
289 // TODO: Error here?
290 break;
291 }
292 }
293
294 return result;
295}
296
297check_snmp_evaluation evaluate_single_unit(response_value response,
298 check_snmp_evaluation_parameters eval_params,
299 check_snmp_test_unit test_unit, time_t query_timestamp,
300 check_snmp_state_entry prev_state,
301 bool have_previous_state) {
302 mp_subcheck sc_oid_test = mp_subcheck_init();
303
304 if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) {
305 xasprintf(&sc_oid_test.output, "%s - ", test_unit.label);
306 } else {
307 sc_oid_test.output = strdup("");
308 }
309
310 char oid_string[(MAX_OID_LEN * 2) + 1] = {};
311
312 int oid_string_result =
313 snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length);
314 if (oid_string_result <= 0) {
315 // TODO error here
316 die(STATE_UNKNOWN, "snprint_objid failed\n");
317 }
318
319 xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string);
320 sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK);
321
322 if (verbose > 2) {
323 printf("Processing oid %s\n", oid_string);
324 }
325
326 bool got_a_numerical_value = false;
327 mp_perfdata_value pd_result_val = {0};
328
329 check_snmp_state_entry result_state = {
330 .timestamp = query_timestamp,
331 .oid_length = response.oid_length,
332 .type = response.type,
333 };
334
335 for (size_t i = 0; i < response.oid_length; i++) {
336 result_state.oid[i] = response.oid[i];
337 }
338
339 if (have_previous_state) {
340 if (query_timestamp == prev_state.timestamp) {
341 // somehow we have the same timestamp again, that can't be good
342 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN);
343 xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid");
344
345 check_snmp_evaluation result = {
346 .sc = sc_oid_test,
347 .state = result_state,
348 };
349
350 return result;
351 }
352 }
353 // compute rate time difference
354 double timeDiff = 0;
355 if (have_previous_state) {
356 if (verbose) {
357 printf("Previous timestamp: %s", ctime(&prev_state.timestamp));
358 printf("Current timestamp: %s", ctime(&query_timestamp));
359 }
360 timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier;
361 }
362
363 mp_perfdata pd_num_val = {};
364
365 switch (response.type) {
366 case ASN_OCTET_STR: {
367 char *tmp = response.string_response;
368 if (strchr(tmp, '"') != NULL) {
369 // got double quote in the string
370 if (strchr(tmp, '\'') != NULL) {
371 // got single quote in the string too
372 // dont quote that at all to avoid even more confusion
373 xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp);
374 } else {
375 // quote with single quotes
376 xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp);
377 }
378 } else {
379 // quote with double quotes
380 xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp);
381 }
382
383 if (strlen(tmp) == 0) {
384 sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result);
385 }
386
387 // String matching test
388 if ((test_unit.eval_mthd.crit_string)) {
389 if (strcmp(tmp, eval_params.string_cmp_value)) {
390 sc_oid_test = mp_set_subcheck_state(
391 sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK);
392 } else {
393 sc_oid_test = mp_set_subcheck_state(
394 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
395 }
396 } else if (test_unit.eval_mthd.crit_regex) {
397 const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1;
398 regmatch_t pmatch[nmatch];
399 memset(pmatch, '\0', sizeof(regmatch_t) * nmatch);
400
401 int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0);
402 if (excode == 0) {
403 sc_oid_test = mp_set_subcheck_state(
404 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
405 } else if (excode != REG_NOMATCH) {
406 char errbuf[MAX_INPUT_BUFFER] = "";
407 regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER);
408 printf(_("Execute Error: %s\n"), errbuf);
409 exit(STATE_CRITICAL);
410 } else { // REG_NOMATCH
411 sc_oid_test = mp_set_subcheck_state(
412 sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK);
413 }
414 }
415 } break;
416 case ASN_COUNTER64:
417 got_a_numerical_value = true;
418
419 result_state.value.uIntVal = response.value.uIntVal;
420 result_state.type = response.type;
421
422 // TODO: perfdata unit counter
423 if (eval_params.calculate_rate && have_previous_state) {
424 if (prev_state.value.uIntVal > response.value.uIntVal) {
425 // overflow
426 unsigned long long tmp =
427 (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal;
428
429 tmp /= timeDiff;
430 pd_result_val = mp_create_pd_value(tmp);
431 } else {
432 pd_result_val = mp_create_pd_value(
433 (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff);
434 }
435 } else {
436 // It's only a counter if we cont compute rate
437 pd_num_val.uom = "c";
438 pd_result_val = mp_create_pd_value(response.value.uIntVal);
439 }
440 break;
441 case ASN_GAUGE: // same as ASN_UNSIGNED
442 case ASN_TIMETICKS:
443 case ASN_COUNTER:
444 case ASN_UINTEGER: {
445 got_a_numerical_value = true;
446 long long treated_value = (long long)response.value.uIntVal;
447
448 if (eval_params.multiplier_set || eval_params.offset_set) {
449 double processed = 0;
450 if (eval_params.offset_set) {
451 processed += eval_params.offset;
452 }
453
454 if (eval_params.multiplier_set) {
455 processed = processed * eval_params.multiplier;
456 }
457
458 treated_value = lround(processed);
459 }
460
461 result_state.value.intVal = treated_value;
462
463 if (eval_params.calculate_rate && have_previous_state) {
464 if (verbose > 2) {
465 printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__,
466 prev_state.value.intVal);
467 printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__,
468 treated_value);
469 }
470 double rate = (treated_value - prev_state.value.intVal) / timeDiff;
471 pd_result_val = mp_create_pd_value(rate);
472 } else {
473 pd_result_val = mp_create_pd_value(treated_value);
474
475 if (response.type == ASN_COUNTER) {
476 pd_num_val.uom = "c";
477 }
478 }
479
480 } break;
481 case ASN_INTEGER: {
482 if (eval_params.multiplier_set || eval_params.offset_set) {
483 double processed = 0;
484 if (eval_params.multiplier_set) {
485 processed = (double)response.value.intVal * eval_params.multiplier;
486 }
487
488 if (eval_params.offset_set) {
489 processed += eval_params.offset;
490 }
491
492 result_state.value.doubleVal = processed;
493
494 if (eval_params.calculate_rate && have_previous_state) {
495 pd_result_val =
496 mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff);
497 } else {
498 pd_result_val = mp_create_pd_value(processed);
499 }
500 } else {
501 result_state.value.intVal = response.value.intVal;
502
503 if (eval_params.calculate_rate && have_previous_state) {
504 pd_result_val = mp_create_pd_value(
505 (response.value.intVal - prev_state.value.intVal) / timeDiff);
506 } else {
507 pd_result_val = mp_create_pd_value(response.value.intVal);
508 }
509 }
510
511 got_a_numerical_value = true;
512 } break;
513 case ASN_FLOAT: // fallthrough
514 case ASN_DOUBLE: {
515 got_a_numerical_value = true;
516 double tmp = response.value.doubleVal;
517 if (eval_params.offset_set) {
518 tmp += eval_params.offset;
519 }
520
521 if (eval_params.multiplier_set) {
522 tmp *= eval_params.multiplier;
523 }
524
525 if (eval_params.calculate_rate && have_previous_state) {
526 pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff);
527 } else {
528 pd_result_val = mp_create_pd_value(tmp);
529 }
530 got_a_numerical_value = true;
531
532 result_state.value.doubleVal = tmp;
533 } break;
534 case ASN_IPADDRESS:
535 // TODO
536 break;
537 }
538
539 if (got_a_numerical_value) {
540 if (eval_params.use_oid_as_perf_data_label) {
541 // Use oid for perdata label
542 pd_num_val.label = strdup(oid_string);
543 // TODO strdup error checking
544 } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) {
545 pd_num_val.label = strdup(test_unit.label);
546 } else {
547 pd_num_val.label = strdup(test_unit.oid);
548 }
549
550 if (!(eval_params.calculate_rate && !have_previous_state)) {
551 // some kind of numerical value
552 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
553 pd_num_val.uom = test_unit.unit_value;
554 }
555
556 pd_num_val.value = pd_result_val;
557
558 xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output,
559 pd_value_to_string(pd_result_val));
560
561 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
562 xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value);
563 }
564
565 if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) {
566 pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold);
567 mp_state_enum tmp_state = mp_get_pd_status(pd_num_val);
568
569 if (tmp_state == STATE_WARNING) {
570 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING);
571 xasprintf(&sc_oid_test.output, "%s - number violates warning threshold",
572 sc_oid_test.output);
573 } else if (tmp_state == STATE_CRITICAL) {
574 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL);
575 xasprintf(&sc_oid_test.output, "%s - number violates critical threshold",
576 sc_oid_test.output);
577 }
578 }
579
580 mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val);
581 } else {
582 // should calculate rate, but there is no previous state, so first run
583 // exit with ok now
584 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK);
585 xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay",
586 sc_oid_test.output);
587 }
588 }
589
590 check_snmp_evaluation result = {
591 .sc = sc_oid_test,
592 .state = result_state,
593 };
594
595 return result;
596}
597
598char *_np_state_generate_key(int argc, char **argv);
599
600/*
601 * If time=NULL, use current time. Create state file, with state format
602 * version, default text. Writes version, time, and data. Avoid locking
603 * problems - use mv to write and then swap. Possible loss of state data if
604 * two things writing to same key at same time.
605 * Will die with UNKNOWN if errors
606 */
607void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) {
608 time_t current_time;
609 if (timestamp == 0) {
610 time(&current_time);
611 } else {
612 current_time = timestamp;
613 }
614
615 int result = 0;
616
617 /* If file doesn't currently exist, create directories */
618 if (access(stateKey._filename, F_OK) != 0) {
619 char *directories = NULL;
620 result = asprintf(&directories, "%s", stateKey._filename);
621 if (result < 0) {
622 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
623 }
624
625 for (char *p = directories + 1; *p; p++) {
626 if (*p == '/') {
627 *p = '\0';
628 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
629 /* Can't free this! Otherwise error message is wrong! */
630 /* np_free(directories); */
631 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
632 }
633 *p = '/';
634 }
635 }
636
637 if (directories) {
638 free(directories);
639 }
640 }
641
642 char *temp_file = NULL;
643 result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename);
644 if (result < 0) {
645 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
646 }
647
648 int temp_file_desc = 0;
649 if ((temp_file_desc = mkstemp(temp_file)) == -1) {
650 if (temp_file) {
651 free(temp_file);
652 }
653 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
654 }
655
656 FILE *temp_file_pointer = fdopen(temp_file_desc, "w");
657 if (temp_file_pointer == NULL) {
658 close(temp_file_desc);
659 unlink(temp_file);
660 if (temp_file) {
661 free(temp_file);
662 }
663 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
664 }
665
666 fprintf(temp_file_pointer, "# NP State file\n");
667 fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION);
668 fprintf(temp_file_pointer, "%d\n", stateKey.data_version);
669 fprintf(temp_file_pointer, "%lu\n", current_time);
670 fprintf(temp_file_pointer, "%s\n", stringToStore);
671
672 fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP);
673
674 fflush(temp_file_pointer);
675
676 result = fclose(temp_file_pointer);
677
678 fsync(temp_file_desc);
679
680 if (result != 0) {
681 unlink(temp_file);
682 if (temp_file) {
683 free(temp_file);
684 }
685 die(STATE_UNKNOWN, _("Error writing temp file"));
686 }
687
688 if (rename(temp_file, stateKey._filename) != 0) {
689 unlink(temp_file);
690 if (temp_file) {
691 free(temp_file);
692 }
693 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
694 }
695
696 if (temp_file) {
697 free(temp_file);
698 }
699}
700
701/*
702 * Read the state file
703 */
704bool _np_state_read_file(FILE *state_file, state_key stateKey) {
705 time_t current_time;
706 time(&current_time);
707
708 /* Note: This introduces a limit of 8192 bytes in the string data */
709 char *line = (char *)calloc(1, 8192);
710 if (line == NULL) {
711 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
712 }
713
714 bool status = false;
715 enum {
716 STATE_FILE_VERSION,
717 STATE_DATA_VERSION,
718 STATE_DATA_TIME,
719 STATE_DATA_TEXT,
720 STATE_DATA_END
721 } expected = STATE_FILE_VERSION;
722
723 int failure = 0;
724 while (!failure && (fgets(line, 8192, state_file)) != NULL) {
725 size_t pos = strlen(line);
726 if (line[pos - 1] == '\n') {
727 line[pos - 1] = '\0';
728 }
729
730 if (line[0] == '#') {
731 continue;
732 }
733
734 switch (expected) {
735 case STATE_FILE_VERSION: {
736 int i = atoi(line);
737 if (i != NP_STATE_FORMAT_VERSION) {
738 failure++;
739 } else {
740 expected = STATE_DATA_VERSION;
741 }
742 } break;
743 case STATE_DATA_VERSION: {
744 int i = atoi(line);
745 if (i != stateKey.data_version) {
746 failure++;
747 } else {
748 expected = STATE_DATA_TIME;
749 }
750 } break;
751 case STATE_DATA_TIME: {
752 /* If time > now, error */
753 time_t data_time = strtoul(line, NULL, 10);
754 if (data_time > current_time) {
755 failure++;
756 } else {
757 stateKey.state_data->time = data_time;
758 expected = STATE_DATA_TEXT;
759 }
760 } break;
761 case STATE_DATA_TEXT:
762 stateKey.state_data->data = strdup(line);
763 if (stateKey.state_data->data == NULL) {
764 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
765 }
766 stateKey.state_data->length = strlen(line);
767 expected = STATE_DATA_END;
768 status = true;
769 break;
770 case STATE_DATA_END:;
771 }
772 }
773
774 if (line) {
775 free(line);
776 }
777 return status;
778}
779/*
780 * Will return NULL if no data is available (first run). If key currently
781 * exists, read data. If state file format version is not expected, return
782 * as if no data. Get state data version number and compares to expected.
783 * If numerically lower, then return as no previous state. die with UNKNOWN
784 * if exceptional error.
785 */
786state_data *np_state_read(state_key stateKey) {
787 /* Open file. If this fails, no previous state found */
788 FILE *statefile = fopen(stateKey._filename, "r");
789 state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data));
790 if (statefile != NULL) {
791
792 if (this_state_data == NULL) {
793 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
794 }
795
796 this_state_data->data = NULL;
797 stateKey.state_data = this_state_data;
798
799 if (_np_state_read_file(statefile, stateKey)) {
800 this_state_data->errorcode = OK;
801 } else {
802 this_state_data->errorcode = ERROR;
803 }
804
805 fclose(statefile);
806 } else {
807 // Failed to open state file
808 this_state_data->errorcode = ERROR;
809 }
810
811 return stateKey.state_data;
812}
813
814/*
815 * Internal function. Returns either:
816 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
817 * statically compiled shared state directory
818 */
819char *_np_state_calculate_location_prefix(void) {
820 char *env_dir;
821
822 /* Do not allow passing MP_STATE_PATH in setuid plugins
823 * for security reasons */
824 if (!mp_suid()) {
825 env_dir = getenv("MP_STATE_PATH");
826 if (env_dir && env_dir[0] != '\0') {
827 return env_dir;
828 }
829 /* This is the former ENV, for backward-compatibility */
830 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
831 if (env_dir && env_dir[0] != '\0') {
832 return env_dir;
833 }
834 }
835
836 return NP_STATE_DIR_PREFIX;
837}
838
839/*
840 * Initiatializer for state routines.
841 * Sets variables. Generates filename. Returns np_state_key. die with
842 * UNKNOWN if exception
843 */
844state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
845 char **argv) {
846 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
847 if (this_state == NULL) {
848 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
849 }
850
851 char *temp_keyname = NULL;
852 if (keyname == NULL) {
853 temp_keyname = _np_state_generate_key(argc, argv);
854 } else {
855 temp_keyname = strdup(keyname);
856 if (temp_keyname == NULL) {
857 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
858 }
859 }
860
861 /* Die if invalid characters used for keyname */
862 char *tmp_char = temp_keyname;
863 while (*tmp_char != '\0') {
864 if (!(isalnum(*tmp_char) || *tmp_char == '_')) {
865 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
866 }
867 tmp_char++;
868 }
869 this_state->name = temp_keyname;
870 this_state->plugin_name = plugin_name;
871 this_state->data_version = expected_data_version;
872 this_state->state_data = NULL;
873
874 /* Calculate filename */
875 char *temp_filename = NULL;
876 int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(),
877 (unsigned long)geteuid(), plugin_name, this_state->name);
878 if (error < 0) {
879 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
880 }
881
882 this_state->_filename = temp_filename;
883
884 return *this_state;
885}
886
887/*
888 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
889 * hopefully a unique key per service/plugin invocation. Use the extra-opts
890 * parse of argv, so that uniqueness in parameters are reflected there.
891 */
892char *_np_state_generate_key(int argc, char **argv) {
893 unsigned char result[256];
894
895#ifdef USE_OPENSSL
896 /*
897 * This code path is chosen if openssl is available (which should be the most common
898 * scenario). Alternatively, the gnulib implementation/
899 *
900 */
901 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
902
903 EVP_DigestInit(ctx, EVP_sha256());
904
905 for (int i = 0; i < argc; i++) {
906 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
907 }
908
909 EVP_DigestFinal(ctx, result, NULL);
910#else
911
912 struct sha256_ctx ctx;
913
914 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
915 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
916 }
917
918 sha256_finish_ctx(&ctx, result);
919#endif // FOUNDOPENSSL
920
921 char keyname[41];
922 for (int i = 0; i < 20; ++i) {
923 sprintf(&keyname[2 * i], "%02x", result[i]);
924 }
925
926 keyname[40] = '\0';
927
928 char *keyname_copy = strdup(keyname);
929 if (keyname_copy == NULL) {
930 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
931 }
932
933 return keyname_copy;
934}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h
new file mode 100644
index 00000000..0f7780b1
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.h
@@ -0,0 +1,71 @@
1#pragma once
2
3#include "./config.h"
4#include <net-snmp/library/asn1.h>
5
6check_snmp_test_unit check_snmp_test_unit_init();
7int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool);
8check_snmp_config check_snmp_config_init();
9
10typedef struct {
11 oid oid[MAX_OID_LEN];
12 size_t oid_length;
13 unsigned char type;
14 union {
15 uint64_t uIntVal;
16 int64_t intVal;
17 double doubleVal;
18 } value;
19 char *string_response;
20} response_value;
21
22typedef struct {
23 int errorcode;
24 response_value *response_values;
25} snmp_responces;
26snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters);
27
28// state is similar to response, but only numerics and a timestamp
29typedef struct {
30 time_t timestamp;
31 oid oid[MAX_OID_LEN];
32 size_t oid_length;
33 unsigned char type;
34 union {
35 unsigned long long uIntVal;
36 long long intVal;
37 double doubleVal;
38 } value;
39} check_snmp_state_entry;
40
41typedef struct {
42 check_snmp_state_entry state;
43 mp_subcheck sc;
44} check_snmp_evaluation;
45check_snmp_evaluation evaluate_single_unit(response_value response,
46 check_snmp_evaluation_parameters eval_params,
47 check_snmp_test_unit test_unit, time_t query_timestamp,
48 check_snmp_state_entry prev_state,
49 bool have_previous_state);
50
51#define NP_STATE_FORMAT_VERSION 1
52
53typedef struct state_data_struct {
54 time_t time;
55 void *data;
56 size_t length; /* Of binary data */
57 int errorcode;
58} state_data;
59
60typedef struct state_key_struct {
61 char *name;
62 char *plugin_name;
63 int data_version;
64 char *_filename;
65 state_data *state_data;
66} state_key;
67
68state_data *np_state_read(state_key stateKey);
69state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
70 char **argv);
71void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore);
diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h
new file mode 100644
index 00000000..e7b6d1b3
--- /dev/null
+++ b/plugins/check_snmp.d/config.h
@@ -0,0 +1,81 @@
1#pragma once
2
3#include "../../lib/thresholds.h"
4#include "../../lib/states.h"
5#include <stdlib.h>
6#include <stdbool.h>
7#include <regex.h>
8#include "../common.h"
9
10// defines for snmp libs
11#define u_char unsigned char
12#define u_long unsigned long
13#define u_short unsigned short
14#define u_int unsigned int
15
16#include <net-snmp/net-snmp-config.h>
17#include <net-snmp/net-snmp-includes.h>
18#include <net-snmp/library/snmp.h>
19#include <net-snmp/session_api.h>
20
21#define DEFAULT_PORT "161"
22#define DEFAULT_RETRIES 5
23
24typedef struct eval_method {
25 bool crit_string;
26 bool crit_regex;
27} eval_method;
28
29typedef struct check_snmp_test_unit {
30 char *oid;
31 char *label;
32 char *unit_value;
33 eval_method eval_mthd;
34 mp_thresholds threshold;
35} check_snmp_test_unit;
36
37typedef struct {
38 struct snmp_session snmp_session;
39 // use getnet instead of get
40 bool use_getnext;
41
42 // TODO actually make these useful
43 bool ignore_mib_parsing_errors;
44 bool need_mibs;
45
46 check_snmp_test_unit *test_units;
47 size_t num_of_test_units;
48} check_snmp_config_snmp_parameters;
49
50typedef struct {
51 // State if an empty value is encountered
52 mp_state_enum nulloid_result;
53
54 // String evaluation stuff
55 bool invert_search;
56 regex_t regex_cmp_value; // regex to match query results against
57 char string_cmp_value[MAX_INPUT_BUFFER];
58
59 // Modify data
60 double multiplier;
61 bool multiplier_set;
62 double offset;
63 bool offset_set;
64
65 // Modify output
66 bool use_oid_as_perf_data_label;
67
68 // activate rate calculation
69 bool calculate_rate;
70 unsigned int rate_multiplier;
71} check_snmp_evaluation_parameters;
72
73typedef struct check_snmp_config {
74 // SNMP session to use
75 check_snmp_config_snmp_parameters snmp_params;
76
77 check_snmp_evaluation_parameters evaluation_params;
78
79 mp_output_format output_format;
80 bool output_format_is_set;
81} check_snmp_config;
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 42a88cf9..f6c8d551 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -28,6 +28,9 @@
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-2024"; 35const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
@@ -35,26 +38,27 @@ const char *email = "devel@monitoring-plugins.org";
35#include "./common.h" 38#include "./common.h"
36#include "./netutils.h" 39#include "./netutils.h"
37#include "utils.h" 40#include "utils.h"
41#include "./check_ssh.d/config.h"
38 42
39#ifndef MSG_DONTWAIT 43#ifndef MSG_DONTWAIT
40# define MSG_DONTWAIT 0 44# define MSG_DONTWAIT 0
41#endif 45#endif
42 46
43#define SSH_DFL_PORT 22 47#define BUFF_SZ 256
44#define BUFF_SZ 256
45 48
46static int port = -1;
47static char *server_name = NULL;
48static char *remote_version = NULL;
49static char *remote_protocol = NULL;
50static bool verbose = false; 49static bool verbose = false;
51 50
52static int process_arguments(int /*argc*/, char ** /*argv*/); 51typedef struct process_arguments_wrapper {
53static int validate_arguments(void); 52 int errorcode;
53 check_ssh_config config;
54} process_arguments_wrapper;
55
56static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
54static void print_help(void); 57static void print_help(void);
55void print_usage(void); 58void print_usage(void);
56 59
57static int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_protocol); 60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version,
61 char *remote_protocol);
58 62
59int main(int argc, char **argv) { 63int main(int argc, char **argv) {
60 setlocale(LC_ALL, ""); 64 setlocale(LC_ALL, "");
@@ -64,51 +68,75 @@ int main(int argc, char **argv) {
64 /* Parse extra opts if any */ 68 /* Parse extra opts if any */
65 argv = np_extra_opts(&argc, argv, progname); 69 argv = np_extra_opts(&argc, argv, progname);
66 70
67 if (process_arguments(argc, argv) == ERROR) 71 process_arguments_wrapper tmp_config = process_arguments(argc, argv);
72
73 if (tmp_config.errorcode == ERROR) {
68 usage4(_("Could not parse arguments")); 74 usage4(_("Could not parse arguments"));
75 }
76
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 }
69 83
70 /* initialize alarm signal handling */ 84 /* initialize alarm signal handling */
71 signal(SIGALRM, socket_timeout_alarm_handler); 85 signal(SIGALRM, socket_timeout_alarm_handler);
72
73 alarm(socket_timeout); 86 alarm(socket_timeout);
74 87
75 /* ssh_connect exits if error is found */ 88 /* ssh_connect exits if error is found */
76 int 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);
77 91
78 alarm(0); 92 alarm(0);
79 93
80 return (result); 94 mp_exit(overall);
81} 95}
82 96
97#define output_format_index CHAR_MAX + 1
98
83/* process command-line arguments */ 99/* process command-line arguments */
84int process_arguments(int argc, char **argv) { 100process_arguments_wrapper process_arguments(int argc, char **argv) {
85 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 101 static struct option longopts[] = {
86 {"version", no_argument, 0, 'V'}, 102 {"help", no_argument, 0, 'h'},
87 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 103 {"version", no_argument, 0, 'V'},
88 {"hostname", required_argument, 0, 'H'}, 104 {"host", required_argument, 0, 'H'}, /* backward compatibility */
89 {"port", required_argument, 0, 'p'}, 105 {"hostname", required_argument, 0, 'H'},
90 {"use-ipv4", no_argument, 0, '4'}, 106 {"port", required_argument, 0, 'p'},
91 {"use-ipv6", no_argument, 0, '6'}, 107 {"use-ipv4", no_argument, 0, '4'},
92 {"timeout", required_argument, 0, 't'}, 108 {"use-ipv6", no_argument, 0, '6'},
93 {"verbose", no_argument, 0, 'v'}, 109 {"timeout", required_argument, 0, 't'},
94 {"remote-version", required_argument, 0, 'r'}, 110 {"verbose", no_argument, 0, 'v'},
95 {"remote-protocol", required_argument, 0, 'P'}, 111 {"remote-version", required_argument, 0, 'r'},
96 {0, 0, 0, 0}}; 112 {"remote-protocol", required_argument, 0, 'P'},
97 113 {"output-format", required_argument, 0, output_format_index},
98 if (argc < 2) 114 {0, 0, 0, 0}};
99 return ERROR; 115
100 116 process_arguments_wrapper result = {
101 for (int i = 1; i < argc; i++) 117 .config = check_ssh_config_init(),
102 if (strcmp("-to", argv[i]) == 0) 118 .errorcode = OK,
119 };
120
121 if (argc < 2) {
122 result.errorcode = ERROR;
123 return result;
124 }
125
126 for (int i = 1; i < argc; i++) {
127 if (strcmp("-to", argv[i]) == 0) {
103 strcpy(argv[i], "-t"); 128 strcpy(argv[i], "-t");
129 }
130 }
104 131
105 int option_char; 132 int option_char;
106 while (true) { 133 while (true) {
107 int option = 0; 134 int option = 0;
108 option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option); 135 option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option);
109 136
110 if (option_char == -1 || option_char == EOF) 137 if (option_char == -1 || option_char == EOF) {
111 break; 138 break;
139 }
112 140
113 switch (option_char) { 141 switch (option_char) {
114 case '?': /* help */ 142 case '?': /* help */
@@ -123,10 +151,11 @@ int process_arguments(int argc, char **argv) {
123 verbose = true; 151 verbose = true;
124 break; 152 break;
125 case 't': /* timeout period */ 153 case 't': /* timeout period */
126 if (!is_integer(optarg)) 154 if (!is_intpos(optarg)) {
127 usage2(_("Timeout interval must be a positive integer"), optarg); 155 usage2(_("Timeout interval must be a positive integer"), optarg);
128 else 156 } else {
129 socket_timeout = atoi(optarg); 157 socket_timeout = (unsigned int)atoi(optarg);
158 }
130 break; 159 break;
131 case '4': 160 case '4':
132 address_family = AF_INET; 161 address_family = AF_INET;
@@ -139,50 +168,61 @@ int process_arguments(int argc, char **argv) {
139#endif 168#endif
140 break; 169 break;
141 case 'r': /* remote version */ 170 case 'r': /* remote version */
142 remote_version = optarg; 171 result.config.remote_version = optarg;
143 break; 172 break;
144 case 'P': /* remote version */ 173 case 'P': /* remote version */
145 remote_protocol = optarg; 174 result.config.remote_protocol = optarg;
146 break; 175 break;
147 case 'H': /* host */ 176 case 'H': /* host */
148 if (!is_host(optarg)) 177 if (!is_host(optarg)) {
149 usage2(_("Invalid hostname/address"), optarg); 178 usage2(_("Invalid hostname/address"), optarg);
150 server_name = optarg; 179 }
180 result.config.server_name = optarg;
151 break; 181 break;
152 case 'p': /* port */ 182 case 'p': /* port */
153 if (is_intpos(optarg)) { 183 if (is_intpos(optarg)) {
154 port = atoi(optarg); 184 result.config.port = atoi(optarg);
155 } else { 185 } else {
156 usage2(_("Port number must be a positive integer"), optarg); 186 usage2(_("Port number must be a positive integer"), optarg);
157 } 187 }
188 break;
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);
195 }
196
197 result.config.output_format_is_set = true;
198 result.config.output_format = parser.output_format;
199 break;
200 }
158 } 201 }
159 } 202 }
160 203
161 option_char = optind; 204 option_char = optind;
162 if (server_name == NULL && option_char < argc) { 205 if (result.config.server_name == NULL && option_char < argc) {
163 if (is_host(argv[option_char])) { 206 if (is_host(argv[option_char])) {
164 server_name = argv[option_char++]; 207 result.config.server_name = argv[option_char++];
165 } 208 }
166 } 209 }
167 210
168 if (port == -1 && option_char < argc) { 211 if (result.config.port == -1 && option_char < argc) {
169 if (is_intpos(argv[option_char])) { 212 if (is_intpos(argv[option_char])) {
170 port = atoi(argv[option_char++]); 213 result.config.port = atoi(argv[option_char++]);
171 } else { 214 } else {
172 print_usage(); 215 print_usage();
173 exit(STATE_UNKNOWN); 216 exit(STATE_UNKNOWN);
174 } 217 }
175 } 218 }
176 219
177 return validate_arguments(); 220 if (result.config.server_name == NULL) {
178} 221 result.errorcode = ERROR;
222 return result;
223 }
179 224
180int validate_arguments(void) { 225 return result;
181 if (server_name == NULL)
182 return ERROR;
183 if (port == -1) /* funky, but allows -p to override stray integer in args */
184 port = SSH_DFL_PORT;
185 return OK;
186} 226}
187 227
188/************************************************************************ 228/************************************************************************
@@ -191,36 +231,45 @@ int validate_arguments(void) {
191 * 231 *
192 *-----------------------------------------------------------------------*/ 232 *-----------------------------------------------------------------------*/
193 233
194int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_protocol) { 234int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version,
235 char *desired_remote_protocol) {
195 struct timeval tv; 236 struct timeval tv;
196 gettimeofday(&tv, NULL); 237 gettimeofday(&tv, NULL);
197 238
198 int socket; 239 int socket;
199 int result = my_tcp_connect(haddr, hport, &socket); 240 int result = my_tcp_connect(haddr, hport, &socket);
200 241
201 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);
202 return result; 248 return result;
249 }
203 250
204 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); 251 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
205 char *buffer = NULL; 252 char *buffer = NULL;
206 ssize_t recv_ret = 0; 253 ssize_t recv_ret = 0;
207 char *version_control_string = NULL; 254 char *version_control_string = NULL;
208 ssize_t byte_offset = 0; 255 size_t byte_offset = 0;
209 while ((version_control_string == NULL) && (recv_ret = recv(socket, 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)) {
210 259
211 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*/
212 byte_offset = 0; 261 byte_offset = 0;
213 262
214 char *index = NULL; 263 char *index = NULL;
215 int len = 0;
216 while ((index = strchr(output + byte_offset, '\n')) != NULL) { 264 while ((index = strchr(output + byte_offset, '\n')) != NULL) {
217 /*Partition the buffer so that this line is a separate string, 265 /*Partition the buffer so that this line is a separate string,
218 * by replacing the newline with NUL*/ 266 * by replacing the newline with NUL*/
219 output[(index - output)] = '\0'; 267 output[(index - output)] = '\0';
220 len = strlen(output + byte_offset); 268 size_t len = strlen(output + byte_offset);
221 269
222 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { 270 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) {
223 /*if the string starts with SSH-, this _should_ be a valid version control string*/ 271 /*if the string starts with SSH-, this _should_ be a valid version control
272 * string*/
224 version_control_string = output + byte_offset; 273 version_control_string = output + byte_offset;
225 break; 274 break;
226 } 275 }
@@ -230,27 +279,38 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
230 } 279 }
231 280
232 if (version_control_string == NULL) { 281 if (version_control_string == NULL) {
233 /* move unconsumed data to beginning of buffer, null rest */ 282 /* move unconsumed data to beginning of buffer */
234 memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); 283 memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
235 memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1);
236 284
237 /*start reading from end of current line chunk on next recv*/ 285 /*start reading from end of current line chunk on next recv*/
238 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);
239 } 290 }
240 } else { 291 } else {
241 byte_offset += recv_ret; 292 byte_offset += (size_t)recv_ret;
242 } 293 }
243 } 294 }
244 295
245 if (recv_ret < 0) { 296 if (recv_ret < 0) {
246 printf("SSH CRITICAL - %s", strerror(errno)); 297 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
247 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;
248 } 301 }
249 302
250 if (version_control_string == NULL) { 303 if (version_control_string == NULL) {
251 printf("SSH CRITICAL - No version control string received"); 304 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
252 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;
253 } 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
254 /* 314 /*
255 * "When the connection has been established, both sides MUST send an 315 * "When the connection has been established, both sides MUST send an
256 * identification string. This identification string MUST be 316 * identification string. This identification string MUST be
@@ -259,8 +319,9 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
259 * - RFC 4253:4.2 319 * - RFC 4253:4.2
260 */ 320 */
261 strip(version_control_string); 321 strip(version_control_string);
262 if (verbose) 322 if (verbose) {
263 printf("%s\n", version_control_string); 323 printf("%s\n", version_control_string);
324 }
264 325
265 char *ssh_proto = version_control_string + 4; 326 char *ssh_proto = version_control_string + 4;
266 327
@@ -280,7 +341,8 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
280 * "1.x" (e.g., "1.5" or "1.3")." 341 * "1.x" (e.g., "1.5" or "1.3")."
281 * - RFC 4253:5 342 * - RFC 4253:5
282 */ 343 */
283 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ 344 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") +
345 1; /* (+1 for the '-' separating protoversion from softwareversion) */
284 346
285 /* 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
286 * (which is NOT part of the server name/version)*/ 348 * (which is NOT part of the server name/version)*/
@@ -288,41 +350,69 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
288 if (tmp) { 350 if (tmp) {
289 ssh_server[tmp - ssh_server] = '\0'; 351 ssh_server[tmp - ssh_server] = '\0';
290 } 352 }
353
354 mp_subcheck protocol_validity_sc = mp_subcheck_init();
291 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { 355 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
292 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);
293 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;
294 } 361 }
362
363 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
364 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s",
365 version_control_string);
366 mp_add_subcheck_to_check(overall, protocol_validity_sc);
367
295 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; 368 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
296 369
297 static char *rev_no = VERSION; 370 static char *rev_no = VERSION;
298 xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); 371 xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
299 send(socket, buffer, strlen(buffer), MSG_DONTWAIT); 372 send(socket, buffer, strlen(buffer), MSG_DONTWAIT);
300 if (verbose) 373 if (verbose) {
301 printf("%s\n", buffer); 374 printf("%s\n", buffer);
375 }
302 376
303 if (remote_version && strcmp(remote_version, ssh_server)) { 377 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
304 printf(_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"), ssh_server, ssh_proto, remote_version); 378 mp_subcheck remote_version_sc = mp_subcheck_init();
379 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
380 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"),
381 ssh_server, ssh_proto, desired_remote_version);
305 close(socket); 382 close(socket);
306 exit(STATE_CRITICAL); 383 mp_add_subcheck_to_check(overall, remote_version_sc);
384 return OK;
307 } 385 }
308 386
309 double elapsed_time = (double)deltime(tv) / 1.0e6; 387 double elapsed_time = (double)deltime(tv) / 1.0e6;
310 if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { 388 mp_perfdata time_pd = perfdata_init();
311 printf(_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s' | %s\n"), ssh_server, ssh_proto, remote_protocol, 389 time_pd.value = mp_create_pd_value(elapsed_time);
312 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, true, (int)socket_timeout)); 390 time_pd.label = "time";
313 close(socket); 391 time_pd.max_present = true;
314 exit(STATE_CRITICAL); 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);
315 } 406 }
316 407
317 printf(_("SSH OK - %s (protocol %s) | %s\n"), ssh_server, ssh_proto, 408 mp_add_subcheck_to_check(overall, protocol_version_sc);
318 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, true, (int)socket_timeout));
319 close(socket); 409 close(socket);
320 exit(STATE_OK); 410 return OK;
321} 411}
322 412
323void print_help(void) { 413void print_help(void) {
324 char *myport; 414 char *myport;
325 xasprintf(&myport, "%d", SSH_DFL_PORT); 415 xasprintf(&myport, "%d", default_ssh_port);
326 416
327 print_revision(progname, NP_VERSION); 417 print_revision(progname, NP_VERSION);
328 418
@@ -345,10 +435,12 @@ void print_help(void) {
345 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 435 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
346 436
347 printf(" %s\n", "-r, --remote-version=STRING"); 437 printf(" %s\n", "-r, --remote-version=STRING");
348 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)"));
349 440
350 printf(" %s\n", "-P, --remote-protocol=STRING"); 441 printf(" %s\n", "-P, --remote-protocol=STRING");
351 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);
352 444
353 printf(UT_VERBOSE); 445 printf(UT_VERBOSE);
354 446
@@ -357,5 +449,6 @@ void print_help(void) {
357 449
358void print_usage(void) { 450void print_usage(void) {
359 printf("%s\n", _("Usage:")); 451 printf("%s\n", _("Usage:"));
360 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname); 452 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n",
453 progname);
361} 454}
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 bc90a90b..dbf53a00 100644
--- a/plugins/check_swap.c
+++ b/plugins/check_swap.c
@@ -28,6 +28,9 @@
28 *****************************************************************************/ 28 *****************************************************************************/
29 29
30#include "common.h" 30#include "common.h"
31#include "output.h"
32#include "states.h"
33#include <limits.h>
31#ifdef HAVE_DECL_SWAPCTL 34#ifdef HAVE_DECL_SWAPCTL
32# ifdef HAVE_SYS_PARAM_H 35# ifdef HAVE_SYS_PARAM_H
33# include <sys/param.h> 36# include <sys/param.h>
@@ -69,8 +72,6 @@ int main(int argc, char **argv) {
69 bindtextdomain(PACKAGE, LOCALEDIR); 72 bindtextdomain(PACKAGE, LOCALEDIR);
70 textdomain(PACKAGE); 73 textdomain(PACKAGE);
71 74
72 char *status = strdup("");
73
74 /* Parse extra opts if any */ 75 /* Parse extra opts if any */
75 argv = np_extra_opts(&argc, argv, progname); 76 argv = np_extra_opts(&argc, argv, progname);
76 77
@@ -89,60 +90,112 @@ int main(int argc, char **argv) {
89 exit(STATE_UNKNOWN); 90 exit(STATE_UNKNOWN);
90 } 91 }
91 92
93 if (verbose) {
94 printf("Swap retrieval result:\n"
95 "\tFree: %llu\n"
96 "\tUsed: %llu\n"
97 "\tTotal: %llu\n",
98 data.metrics.free, data.metrics.used, data.metrics.total);
99 }
100
92 double percent_used; 101 double percent_used;
102 mp_check overall = mp_check_init();
103 if (config.output_format_is_set) {
104 mp_set_format(config.output_format);
105 }
106 mp_subcheck sc1 = mp_subcheck_init();
107 sc1 = mp_set_subcheck_default_state(sc1, STATE_OK);
108
93 /* if total_swap_mb == 0, let's not divide by 0 */ 109 /* if total_swap_mb == 0, let's not divide by 0 */
94 if (data.metrics.total != 0) { 110 if (data.metrics.total != 0) {
95 percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total); 111 percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total);
96 } else { 112 } else {
97 printf(_("SWAP %s - Swap is either disabled, not present, or of zero " 113 sc1 = mp_set_subcheck_state(sc1, config.no_swap_state);
98 "size."), 114 sc1.output = (char *)_("Swap is either disabled, not present, or of zero size.");
99 state_text(data.statusCode)); 115
100 exit(config.no_swap_state); 116 mp_add_subcheck_to_check(&overall, sc1);
117 mp_exit(overall);
101 } 118 }
102 119
103 if (verbose) { 120 if (verbose) {
104 printf("Computed usage percentage: %g\n", percent_used); 121 printf("Computed usage percentage: %g\n", percent_used);
105 } 122 }
106 123
107 uint64_t warn_print = config.warn.value; 124 mp_perfdata pd = perfdata_init();
108 if (config.warn.is_percentage) { 125 pd.label = "swap";
109 warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT); 126 pd = mp_set_pd_value(pd, data.metrics.free);
127 pd.uom = "B";
128
129 if (config.warn_is_set) {
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 }
134
135 mp_perfdata_value warn_pd = mp_create_pd_value(warn_print);
136
137 mp_range warn_range = mp_range_init();
138 warn_range.end_infinity = false;
139 warn_range.end = warn_pd;
140
141 pd.warn = warn_range;
142 pd.warn_present = true;
110 } 143 }
111 144
112 uint64_t crit_print = config.crit.value; 145 if (config.crit_is_set) {
113 if (config.crit.is_percentage) { 146 uint64_t crit_print = config.crit.value;
114 crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT); 147 if (config.crit.is_percentage) {
148 crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT);
149 }
150
151 mp_perfdata_value crit_pd = mp_create_pd_value(crit_print);
152
153 mp_range crit_range = mp_range_init();
154 crit_range.end_infinity = false;
155 crit_range.end = crit_pd;
156
157 pd.crit = crit_range;
158 pd.crit_present = true;
115 } 159 }
116 160
117 char *perfdata = perfdata_uint64("swap", data.metrics.free, "B", config.warn_is_set, warn_print, config.crit_is_set, crit_print, true, 161 mp_perfdata_value max = mp_create_pd_value(data.metrics.total);
118 0, true, data.metrics.total); 162 pd.max = max;
163 pd.max_present = true;
119 164
120 if (config.warn_is_set) { 165 mp_perfdata_value min = mp_create_pd_value(0);
121 if (verbose > 1) { 166 pd.min = min;
122 printf("Warn threshold value: %" PRIu64 "\n", config.warn.value); 167 pd.min_present = true;
123 } 168
169 mp_add_perfdata_to_subcheck(&sc1, pd);
170 if (verbose > 1) {
171 printf("Warn threshold value: %" PRIu64 "\n", config.warn.value);
172 }
124 173
125 if ((config.warn.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.warn.value))) || 174 if (config.warn_is_set) {
175 if ((config.warn.is_percentage && (percent_used >= (100 - (double)config.warn.value))) ||
126 config.warn.value >= data.metrics.free) { 176 config.warn.value >= data.metrics.free) {
127 data.statusCode = max_state(data.statusCode, STATE_WARNING); 177 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
128 } 178 }
129 } 179 }
130 180
131 if (config.crit_is_set) { 181 if (verbose > 1) {
132 if (verbose > 1) { 182 printf("Crit threshold value: %" PRIu64 "\n", config.crit.value);
133 printf("Crit threshold value: %" PRIu64 "\n", config.crit.value); 183 }
134 }
135 184
136 if ((config.crit.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.crit.value))) || 185 if (config.crit_is_set) {
186 if ((config.crit.is_percentage && (percent_used >= (100 - (double)config.crit.value))) ||
137 config.crit.value >= data.metrics.free) { 187 config.crit.value >= data.metrics.free) {
138 data.statusCode = max_state(data.statusCode, STATE_CRITICAL); 188 sc1 = mp_set_subcheck_state(sc1, STATE_CRITICAL);
139 } 189 }
140 } 190 }
141 191
142 printf(_("SWAP %s - %g%% free (%lluMiB out of %lluMiB) %s|%s\n"), state_text(data.statusCode), (HUNDRED_PERCENT - percent_used), 192 xasprintf(&sc1.output, _("%g%% free (%lluMiB out of %lluMiB)"), (100 - percent_used),
143 BYTES_TO_MiB(data.metrics.free), BYTES_TO_MiB(data.metrics.total), status, perfdata); 193 data.metrics.free >> 20, data.metrics.total >> 20);
144 194
145 exit(data.statusCode); 195 overall.summary = "Swap";
196 mp_add_subcheck_to_check(&overall, sc1);
197
198 mp_exit(overall);
146} 199}
147 200
148int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) { 201int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
@@ -150,7 +203,9 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
150 return config.no_swap_state; 203 return config.no_swap_state;
151 } 204 }
152 205
153 uint64_t free_swap = (uint64_t)(free_swap_mb * (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */ 206 uint64_t free_swap =
207 (uint64_t)(free_swap_mb *
208 (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */
154 209
155 if (!config.crit.is_percentage && config.crit.value >= free_swap) { 210 if (!config.crit.is_percentage && config.crit.value >= free_swap) {
156 return STATE_CRITICAL; 211 return STATE_CRITICAL;
@@ -159,28 +214,38 @@ int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) {
159 return STATE_WARNING; 214 return STATE_WARNING;
160 } 215 }
161 216
162 uint64_t usage_percentage = (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT; 217 uint64_t usage_percentage =
218 (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT;
163 219
164 if (config.crit.is_percentage && config.crit.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) { 220 if (config.crit.is_percentage && config.crit.value != 0 &&
221 usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) {
165 return STATE_CRITICAL; 222 return STATE_CRITICAL;
166 } 223 }
167 224
168 if (config.warn.is_percentage && config.warn.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) { 225 if (config.warn.is_percentage && config.warn.value != 0 &&
226 usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) {
169 return STATE_WARNING; 227 return STATE_WARNING;
170 } 228 }
171 229
172 return STATE_OK; 230 return STATE_OK;
173} 231}
174 232
233#define output_format_index CHAR_MAX + 1
234
175/* process command-line arguments */ 235/* process command-line arguments */
176swap_config_wrapper process_arguments(int argc, char **argv) { 236swap_config_wrapper process_arguments(int argc, char **argv) {
177 swap_config_wrapper conf_wrapper = {.errorcode = OK}; 237 swap_config_wrapper conf_wrapper = {.errorcode = OK};
178 conf_wrapper.config = swap_config_init(); 238 conf_wrapper.config = swap_config_init();
179 239
180 static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, 240 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
181 {"allswaps", no_argument, 0, 'a'}, {"no-swap", required_argument, 0, 'n'}, 241 {"critical", required_argument, 0, 'c'},
182 {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 242 {"allswaps", no_argument, 0, 'a'},
183 {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; 243 {"no-swap", required_argument, 0, 'n'},
244 {"verbose", no_argument, 0, 'v'},
245 {"version", no_argument, 0, 'V'},
246 {"help", no_argument, 0, 'h'},
247 {"output-format", required_argument, 0, output_format_index},
248 {0, 0, 0, 0}};
184 249
185 while (true) { 250 while (true) {
186 int option = 0; 251 int option = 0;
@@ -263,6 +328,18 @@ swap_config_wrapper process_arguments(int argc, char **argv) {
263 case 'v': /* verbose */ 328 case 'v': /* verbose */
264 verbose++; 329 verbose++;
265 break; 330 break;
331 case output_format_index: {
332 parsed_output_format parser = mp_parse_output_format(optarg);
333 if (!parser.parsing_success) {
334 // TODO List all available formats here, maybe add anothoer usage function
335 printf("Invalid output format: %s\n", optarg);
336 exit(STATE_UNKNOWN);
337 }
338
339 conf_wrapper.config.output_format_is_set = true;
340 conf_wrapper.config.output_format = parser.output_format;
341 break;
342 }
266 case 'V': /* version */ 343 case 'V': /* version */
267 print_revision(progname, NP_VERSION); 344 print_revision(progname, NP_VERSION);
268 exit(STATE_UNKNOWN); 345 exit(STATE_UNKNOWN);
@@ -319,6 +396,7 @@ void print_help(swap_config config) {
319 _("Resulting state when there is no swap regardless of thresholds. " 396 _("Resulting state when there is no swap regardless of thresholds. "
320 "Default:"), 397 "Default:"),
321 state_text(config.no_swap_state)); 398 state_text(config.no_swap_state));
399 printf(UT_OUTPUT_FORMAT);
322 printf(UT_VERBOSE); 400 printf(UT_VERBOSE);
323 401
324 printf("\n"); 402 printf("\n");
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h
index 99039b21..8d3c7fcf 100644
--- a/plugins/check_swap.d/check_swap.h
+++ b/plugins/check_swap.d/check_swap.h
@@ -1,6 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../common.h" 3#include "../common.h"
4#include "../../lib/output.h"
5#include "../../lib/states.h"
4 6
5#ifndef SWAP_CONVERSION 7#ifndef SWAP_CONVERSION
6# define SWAP_CONVERSION 1 8# define SWAP_CONVERSION 1
@@ -25,19 +27,23 @@ typedef struct {
25 27
26typedef struct { 28typedef struct {
27 bool allswaps; 29 bool allswaps;
28 int no_swap_state; 30 mp_state_enum no_swap_state;
29 bool warn_is_set; 31 bool warn_is_set;
30 check_swap_threshold warn; 32 check_swap_threshold warn;
31 bool crit_is_set; 33 bool crit_is_set;
32 check_swap_threshold crit; 34 check_swap_threshold crit;
33 bool on_aix; 35 bool on_aix;
34 int conversion_factor; 36 int conversion_factor;
37
38 bool output_format_is_set;
39 mp_output_format output_format;
35} swap_config; 40} swap_config;
36 41
37swap_config swap_config_init(void); 42swap_config swap_config_init(void);
38 43
39swap_result get_swap_data(swap_config config); 44swap_result get_swap_data(swap_config config);
40swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]); 45swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]);
41swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]); 46swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
47 const char swap_format[]);
42swap_result getSwapFromSwapctl_BSD(swap_config config); 48swap_result getSwapFromSwapctl_BSD(swap_config config);
43swap_result getSwapFromSwap_SRV4(swap_config config); 49swap_result getSwapFromSwap_SRV4(swap_config config);
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
index 2fe4544f..58213a3c 100644
--- a/plugins/check_swap.d/swap.c
+++ b/plugins/check_swap.d/swap.c
@@ -14,6 +14,8 @@ swap_config swap_config_init(void) {
14 tmp.warn_is_set = false; 14 tmp.warn_is_set = false;
15 tmp.crit_is_set = false; 15 tmp.crit_is_set = false;
16 16
17 tmp.output_format_is_set = false;
18
17#ifdef _AIX 19#ifdef _AIX
18 tmp.on_aix = true; 20 tmp.on_aix = true;
19#else 21#else
@@ -50,10 +52,10 @@ swap_result get_swap_data(swap_config config) {
50 } 52 }
51# else // HAVE_SWAP 53# else // HAVE_SWAP
52# ifdef CHECK_SWAP_SWAPCTL_SVR4 54# ifdef CHECK_SWAP_SWAPCTL_SVR4
53 return getSwapFromSwapctl_SRV4(); 55 return getSwapFromSwapctl_SRV4(config);
54# else // CHECK_SWAP_SWAPCTL_SVR4 56# else // CHECK_SWAP_SWAPCTL_SVR4
55# ifdef CHECK_SWAP_SWAPCTL_BSD 57# ifdef CHECK_SWAP_SWAPCTL_BSD
56 return getSwapFromSwapctl_BSD(); 58 return getSwapFromSwapctl_BSD(config);
57# else // CHECK_SWAP_SWAPCTL_BSD 59# else // CHECK_SWAP_SWAPCTL_BSD
58# error No way found to retrieve swap 60# error No way found to retrieve swap
59# endif /* CHECK_SWAP_SWAPCTL_BSD */ 61# endif /* CHECK_SWAP_SWAPCTL_BSD */
@@ -66,7 +68,7 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
66 FILE *meminfo_file_ptr; 68 FILE *meminfo_file_ptr;
67 meminfo_file_ptr = fopen(proc_meminfo, "r"); 69 meminfo_file_ptr = fopen(proc_meminfo, "r");
68 70
69 swap_result result = {0}; 71 swap_result result = {};
70 result.errorcode = STATE_UNKNOWN; 72 result.errorcode = STATE_UNKNOWN;
71 73
72 if (meminfo_file_ptr == NULL) { 74 if (meminfo_file_ptr == NULL) {
@@ -76,90 +78,81 @@ swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
76 return result; 78 return result;
77 } 79 }
78 80
79 uint64_t swap_total = 0; 81 unsigned long swap_total = 0;
80 uint64_t swap_used = 0; 82 unsigned long swap_used = 0;
81 uint64_t swap_free = 0; 83 unsigned long swap_free = 0;
82 84
83 bool found_total = false; 85 bool found_total = false;
84 bool found_used = false;
85 bool found_free = false; 86 bool found_free = false;
86 87
87 char input_buffer[MAX_INPUT_BUFFER]; 88 char input_buffer[MAX_INPUT_BUFFER];
88 char str[32]; 89 char str[32];
89 90
90 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) { 91 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
91 uint64_t tmp_KB = 0;
92 92
93 /* 93 /*
94 * The following sscanf call looks for a line looking like: "Swap: 123 94 * The following sscanf call looks for a line looking like: "Swap: 123
95 * 123 123" On which kind of system this format exists, I can not say, 95 * 123 123" which exists on NetBSD (at least),
96 * but I wanted to document this for people who are not adapt with 96 * The unit should be Bytes
97 * sscanf anymore, like me
98 * Also the units used here are unclear and probably wrong
99 */ 97 */
100 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) { 98 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used,
101 99 &swap_free) == 3) {
102 result.metrics.total += swap_total;
103 result.metrics.used += swap_used;
104 result.metrics.free += swap_free;
105
106 found_total = true; 100 found_total = true;
107 found_free = true; 101 found_free = true;
108 found_used = true;
109
110 // Set error 102 // Set error
111 result.errorcode = STATE_OK; 103 result.errorcode = STATE_OK;
104 // Break out of fgets here, since both scanf expressions might match (NetBSD for
105 // example)
106 break;
107 }
112 108
113 /* 109 /*
114 * The following sscanf call looks for lines looking like: 110 * The following sscanf call looks for lines looking like:
115 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least 111 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least
116 * on Debian Linux with a 5.* kernel 112 * on Debian Linux with a 5.* kernel
117 */ 113 */
118 } else { 114 unsigned long tmp_KB = 0;
119 int sscanf_result = sscanf(input_buffer, 115 int sscanf_result = sscanf(input_buffer,
120 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu " 116 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu "
121 "%*[k]%*[B]", 117 "%*[k]%*[B]",
122 str, &tmp_KB); 118 str, &tmp_KB);
123 119
124 if (sscanf_result == 2) { 120 if (sscanf_result == 2) {
125 121
126 if (verbose >= 3) { 122 if (verbose >= 3) {
127 printf("Got %s with %lu\n", str, tmp_KB); 123 printf("Got %s with %lu\n", str, tmp_KB);
128 }
129
130 /* I think this part is always in Kb, so convert to bytes */
131 if (strcmp("Total", str) == 0) {
132 swap_total = tmp_KB * 1000;
133 found_total = true;
134 } else if (strcmp("Free", str) == 0) {
135 swap_free = swap_free + tmp_KB * 1000;
136 found_free = true;
137 found_used = true; // No explicit used metric available
138 } else if (strcmp("Cached", str) == 0) {
139 swap_free = swap_free + tmp_KB * 1000;
140 found_free = true;
141 found_used = true; // No explicit used metric available
142 }
143
144 result.errorcode = STATE_OK;
145 } 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;
146 } 138 }
147 } 139 }
148 140
149 fclose(meminfo_file_ptr); 141 fclose(meminfo_file_ptr);
150 142
151 result.metrics.total = swap_total; 143 result.metrics.total = swap_total;
152 result.metrics.used = swap_total - swap_free;
153 result.metrics.free = swap_free; 144 result.metrics.free = swap_free;
145 result.metrics.used = swap_total - swap_free;
154 146
155 if (!found_free || !found_total || !found_used) { 147 if (!found_free || !found_total) {
156 result.errorcode = STATE_UNKNOWN; 148 result.errorcode = STATE_UNKNOWN;
157 } 149 }
158 150
159 return result; 151 return result;
160} 152}
161 153
162swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]) { 154swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[],
155 const char swap_format[]) {
163 swap_result result = {0}; 156 swap_result result = {0};
164 157
165 char *temp_buffer; 158 char *temp_buffer;
@@ -222,7 +215,8 @@ swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[]
222 used_swap_mb = total_swap_mb - free_swap_mb; 215 used_swap_mb = total_swap_mb - free_swap_mb;
223 216
224 if (verbose >= 3) { 217 if (verbose >= 3) {
225 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb); 218 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb,
219 free_swap_mb);
226 } 220 }
227 } else { 221 } else {
228 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { 222 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
@@ -295,8 +289,14 @@ struct swapent {
295}; 289};
296 290
297#else 291#else
292
293// Includes for NetBSD
294# include <unistd.h>
295# include <sys/swap.h>
296
298# define bsd_swapctl swapctl 297# define bsd_swapctl swapctl
299#endif 298
299#endif // CHECK_SWAP_SWAPCTL_BSD
300 300
301swap_result getSwapFromSwapctl_BSD(swap_config config) { 301swap_result getSwapFromSwapctl_BSD(swap_config config) {
302 /* get the number of active swap devices */ 302 /* get the number of active swap devices */
@@ -320,8 +320,8 @@ swap_result getSwapFromSwapctl_BSD(swap_config config) {
320 unsigned long long used_swap_mb = 0; 320 unsigned long long used_swap_mb = 0;
321 321
322 for (int i = 0; i < nswaps; i++) { 322 for (int i = 0; i < nswaps; i++) {
323 dsktotal_mb = (float)ent[i].se_nblks / (float)config.conversion_factor; 323 dsktotal_mb = (double)ent[i].se_nblks / (double)config.conversion_factor;
324 dskused_mb = (float)ent[i].se_inuse / (float)config.conversion_factor; 324 dskused_mb = (double)ent[i].se_inuse / (double)config.conversion_factor;
325 dskfree_mb = (dsktotal_mb - dskused_mb); 325 dskfree_mb = (dsktotal_mb - dskused_mb);
326 326
327 if (config.allswaps && dsktotal_mb > 0) { 327 if (config.allswaps && dsktotal_mb > 0) {
@@ -402,7 +402,8 @@ swap_result getSwapFromSwap_SRV4(swap_config config) {
402 } 402 }
403 403
404 /* initialize swap table + entries */ 404 /* initialize swap table + entries */
405 swaptbl_t *tbl = (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps)); 405 swaptbl_t *tbl =
406 (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
406 407
407 if (tbl == NULL) { 408 if (tbl == NULL) {
408 die(STATE_UNKNOWN, _("malloc() failed!\n")); 409 die(STATE_UNKNOWN, _("malloc() failed!\n"));
@@ -437,7 +438,8 @@ swap_result getSwapFromSwap_SRV4(swap_config config) {
437 dskused_mb = (dsktotal_mb - dskfree_mb); 438 dskused_mb = (dsktotal_mb - dskfree_mb);
438 439
439 if (verbose >= 3) { 440 if (verbose >= 3) {
440 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb); 441 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb,
442 dskused_mb);
441 } 443 }
442 444
443 if (config.allswaps && dsktotal_mb > 0) { 445 if (config.allswaps && dsktotal_mb > 0) {
diff --git a/plugins/check_tcp.c b/plugins/check_tcp.c
index 49ad096c..09806373 100644
--- a/plugins/check_tcp.c
+++ b/plugins/check_tcp.c
@@ -3,7 +3,7 @@
3 * Monitoring check_tcp plugin 3 * Monitoring check_tcp plugin
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team 6 * Copyright (c) 1999-2025 Monitoring Plugins Development Team
7 * 7 *
8 * Description: 8 * Description:
9 * 9 *
@@ -29,74 +29,64 @@
29 29
30/* progname "check_tcp" changes depending on symlink called */ 30/* progname "check_tcp" changes depending on symlink called */
31char *progname; 31char *progname;
32const char *copyright = "1999-2024"; 32const char *copyright = "1999-2025";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "netutils.h" 36#include "./netutils.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_tcp.h" 38#include "./check_tcp.d/config.h"
39#include "output.h"
40#include "states.h"
39 41
42#include <sys/types.h>
40#include <ctype.h> 43#include <ctype.h>
41#include <sys/select.h> 44#include <sys/select.h>
42 45
46ssize_t my_recv(int socket_descriptor, char *buf, size_t len, bool use_tls) {
43#ifdef HAVE_SSL 47#ifdef HAVE_SSL
44static bool check_cert = false; 48 if (use_tls) {
45static int days_till_exp_warn, days_till_exp_crit; 49 return np_net_ssl_read(buf, (int)len);
46# define my_recv(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) 50 }
47# define my_send(buf, len) ((flags & FLAG_SSL) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) 51#endif
48#else 52 return read(socket_descriptor, buf, len);
49# define my_recv(buf, len) read(sd, buf, len) 53}
50# define my_send(buf, len) send(sd, buf, len, 0) 54
55ssize_t my_send(int socket_descriptor, char *buf, size_t len, bool use_tls) {
56#ifdef HAVE_SSL
57 if (use_tls) {
58 return np_net_ssl_write(buf, (int)len);
59 }
51#endif 60#endif
61 return write(socket_descriptor, buf, len);
62}
52 63
53/* int my_recv(char *, size_t); */ 64typedef struct {
54static int process_arguments(int /*argc*/, char ** /*argv*/); 65 int errorcode;
55static void print_help(void); 66 check_tcp_config config;
67} check_tcp_config_wrapper;
68static check_tcp_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/,
69 check_tcp_config /*config*/);
70void print_help(const char *service);
56void print_usage(void); 71void print_usage(void);
57 72
58#define EXPECT server_expect[0] 73int verbosity = 0;
59static char *SERVICE = "TCP";
60static char *SEND = NULL;
61static char *QUIT = NULL;
62static int PROTOCOL = IPPROTO_TCP; /* most common is default */
63static int PORT = 0;
64static int READ_TIMEOUT = 2;
65
66static int server_port = 0;
67static char *server_address = NULL;
68static bool host_specified = false;
69static char *server_send = NULL;
70static char *server_quit = NULL;
71static char **server_expect;
72static size_t server_expect_count = 0;
73static ssize_t maxbytes = 0;
74static char **warn_codes = NULL;
75static size_t warn_codes_count = 0;
76static char **crit_codes = NULL;
77static size_t crit_codes_count = 0;
78static unsigned int delay = 0;
79static double warning_time = 0;
80static double critical_time = 0;
81static double elapsed_time = 0;
82static long microsec;
83static int sd = 0;
84#define MAXBUF 1024
85static char buffer[MAXBUF];
86static int expect_mismatch_state = STATE_WARNING;
87static int match_flags = NP_MATCH_EXACT;
88 74
89#ifdef HAVE_SSL 75static const int READ_TIMEOUT = 2;
90static char *sni = NULL; 76
91static bool sni_specified = false; 77const int MAXBUF = 1024;
92#endif
93 78
94#define FLAG_SSL 0x01 79const int DEFAULT_FTP_PORT = 21;
95#define FLAG_VERBOSE 0x02 80const int DEFAULT_POP_PORT = 110;
96#define FLAG_TIME_WARN 0x04 81const int DEFAULT_SPOP_PORT = 995;
97#define FLAG_TIME_CRIT 0x08 82const int DEFAULT_SMTP_PORT = 25;
98#define FLAG_HIDE_OUTPUT 0x10 83const int DEFAULT_SSMTP_PORT = 465;
99static size_t flags; 84const int DEFAULT_IMAP_PORT = 143;
85const int DEFAULT_SIMAP_PORT = 993;
86const int DEFAULT_XMPP_C2S_PORT = 5222;
87const int DEFAULT_NNTP_PORT = 119;
88const int DEFAULT_NNTPS_PORT = 563;
89const int DEFAULT_CLAMD_PORT = 3310;
100 90
101int main(int argc, char **argv) { 91int main(int argc, char **argv) {
102 setlocale(LC_ALL, ""); 92 setlocale(LC_ALL, "");
@@ -105,353 +95,482 @@ int main(int argc, char **argv) {
105 95
106 /* determine program- and service-name quickly */ 96 /* determine program- and service-name quickly */
107 progname = strrchr(argv[0], '/'); 97 progname = strrchr(argv[0], '/');
108 if (progname != NULL) 98 if (progname != NULL) {
109 progname++; 99 progname++;
110 else 100 } else {
111 progname = argv[0]; 101 progname = argv[0];
102 }
103
104 // Initialize config here with values from above,
105 // might be changed by on disk config or cli commands
106 check_tcp_config config = check_tcp_config_init();
112 107
113 size_t prog_name_len = strlen(progname); 108 size_t prog_name_len = strlen(progname);
114 if (prog_name_len > 6 && !memcmp(progname, "check_", 6)) { 109 const size_t prefix_length = strlen("check_");
115 SERVICE = strdup(progname + 6); 110
116 for (size_t i = 0; i < prog_name_len - 6; i++) 111 if (prog_name_len <= prefix_length) {
117 SERVICE[i] = toupper(SERVICE[i]); 112 die(STATE_UNKNOWN, _("Weird progname"));
113 }
114
115 if (!memcmp(progname, "check_", prefix_length)) {
116 config.service = strdup(progname + prefix_length);
117 if (config.service == NULL) {
118 die(STATE_UNKNOWN, _("Allocation failed"));
119 }
120
121 for (size_t i = 0; i < prog_name_len - prefix_length; i++) {
122 config.service[i] = toupper(config.service[i]);
123 }
118 } 124 }
119 125
120 /* set up a reasonable buffer at first (will be realloc()'ed if 126 /* set up a reasonable buffer at first (will be realloc()'ed if
121 * user specifies other options) */ 127 * user specifies other options) */
122 server_expect = calloc(2, sizeof(char *)); 128 config.server_expect = calloc(2, sizeof(char *));
129
130 if (config.server_expect == NULL) {
131 die(STATE_UNKNOWN, _("Allocation failed"));
132 }
123 133
124 /* determine defaults for this service's protocol */ 134 /* determine defaults for this service's protocol */
125 if (!strncmp(SERVICE, "UDP", 3)) { 135 if (!strncmp(config.service, "UDP", strlen("UDP"))) {
126 PROTOCOL = IPPROTO_UDP; 136 config.protocol = IPPROTO_UDP;
127 } else if (!strncmp(SERVICE, "FTP", 3)) { 137 } else if (!strncmp(config.service, "FTP", strlen("FTP"))) {
128 EXPECT = "220"; 138 config.server_expect[0] = "220";
129 QUIT = "QUIT\r\n"; 139 config.quit = "QUIT\r\n";
130 PORT = 21; 140 config.server_port = DEFAULT_FTP_PORT;
131 } else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) { 141 } else if (!strncmp(config.service, "POP", strlen("POP")) ||
132 EXPECT = "+OK"; 142 !strncmp(config.service, "POP3", strlen("POP3"))) {
133 QUIT = "QUIT\r\n"; 143 config.server_expect[0] = "+OK";
134 PORT = 110; 144 config.quit = "QUIT\r\n";
135 } else if (!strncmp(SERVICE, "SMTP", 4)) { 145 config.server_port = DEFAULT_POP_PORT;
136 EXPECT = "220"; 146 } else if (!strncmp(config.service, "SMTP", strlen("SMTP"))) {
137 QUIT = "QUIT\r\n"; 147 config.server_expect[0] = "220";
138 PORT = 25; 148 config.quit = "QUIT\r\n";
139 } else if (!strncmp(SERVICE, "IMAP", 4)) { 149 config.server_port = DEFAULT_SMTP_PORT;
140 EXPECT = "* OK"; 150 } else if (!strncmp(config.service, "IMAP", strlen("IMAP"))) {
141 QUIT = "a1 LOGOUT\r\n"; 151 config.server_expect[0] = "* OK";
142 PORT = 143; 152 config.quit = "a1 LOGOUT\r\n";
153 config.server_port = DEFAULT_IMAP_PORT;
143 } 154 }
144#ifdef HAVE_SSL 155#ifdef HAVE_SSL
145 else if (!strncmp(SERVICE, "SIMAP", 5)) { 156 else if (!strncmp(config.service, "SIMAP", strlen("SIMAP"))) {
146 EXPECT = "* OK"; 157 config.server_expect[0] = "* OK";
147 QUIT = "a1 LOGOUT\r\n"; 158 config.quit = "a1 LOGOUT\r\n";
148 flags |= FLAG_SSL; 159 config.use_tls = true;
149 PORT = 993; 160 config.server_port = DEFAULT_SIMAP_PORT;
150 } else if (!strncmp(SERVICE, "SPOP", 4)) { 161 } else if (!strncmp(config.service, "SPOP", strlen("SPOP"))) {
151 EXPECT = "+OK"; 162 config.server_expect[0] = "+OK";
152 QUIT = "QUIT\r\n"; 163 config.quit = "QUIT\r\n";
153 flags |= FLAG_SSL; 164 config.use_tls = true;
154 PORT = 995; 165 config.server_port = DEFAULT_SPOP_PORT;
155 } else if (!strncmp(SERVICE, "SSMTP", 5)) { 166 } else if (!strncmp(config.service, "SSMTP", strlen("SSMTP"))) {
156 EXPECT = "220"; 167 config.server_expect[0] = "220";
157 QUIT = "QUIT\r\n"; 168 config.quit = "QUIT\r\n";
158 flags |= FLAG_SSL; 169 config.use_tls = true;
159 PORT = 465; 170 config.server_port = DEFAULT_SSMTP_PORT;
160 } else if (!strncmp(SERVICE, "JABBER", 6)) { 171 } else if (!strncmp(config.service, "JABBER", strlen("JABBER"))) {
161 SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n"; 172 config.send = "<stream:stream to=\'host\' xmlns=\'jabber:client\' "
162 EXPECT = "<?xml version=\'1.0\'"; 173 "xmlns:stream=\'http://etherx.jabber.org/streams\'>\n";
163 QUIT = "</stream:stream>\n"; 174 config.server_expect[0] = "<?xml version=\'1.0\'";
164 flags |= FLAG_HIDE_OUTPUT; 175 config.quit = "</stream:stream>\n";
165 PORT = 5222; 176 config.hide_output = true;
166 } else if (!strncmp(SERVICE, "NNTPS", 5)) { 177 config.server_port = DEFAULT_XMPP_C2S_PORT;
167 server_expect_count = 2; 178 } else if (!strncmp(config.service, "NNTPS", strlen("NNTPS"))) {
168 server_expect[0] = "200"; 179 config.server_expect_count = 2;
169 server_expect[1] = "201"; 180 config.server_expect[0] = "200";
170 QUIT = "QUIT\r\n"; 181 config.server_expect[1] = "201";
171 flags |= FLAG_SSL; 182 config.quit = "QUIT\r\n";
172 PORT = 563; 183 config.use_tls = true;
184 config.server_port = DEFAULT_NNTPS_PORT;
173 } 185 }
174#endif 186#endif
175 else if (!strncmp(SERVICE, "NNTP", 4)) { 187 else if (!strncmp(config.service, "NNTP", strlen("NNTP"))) {
176 server_expect_count = 2; 188 config.server_expect_count = 2;
177 server_expect = malloc(sizeof(char *) * server_expect_count); 189 char **tmp = realloc(config.server_expect, config.server_expect_count * sizeof(char *));
178 server_expect[0] = strdup("200"); 190 if (tmp == NULL) {
179 server_expect[1] = strdup("201"); 191 free(config.server_expect);
180 QUIT = "QUIT\r\n"; 192 die(STATE_UNKNOWN, _("Allocation failed"));
181 PORT = 119; 193 }
182 } else if (!strncmp(SERVICE, "CLAMD", 5)) { 194 config.server_expect = tmp;
183 SEND = "PING"; 195
184 EXPECT = "PONG"; 196 config.server_expect[0] = strdup("200");
185 QUIT = NULL; 197 config.server_expect[1] = strdup("201");
186 PORT = 3310; 198 config.quit = "QUIT\r\n";
199 config.server_port = DEFAULT_NNTP_PORT;
200 } else if (!strncmp(config.service, "CLAMD", strlen("CLAMD"))) {
201 config.send = "PING";
202 config.server_expect[0] = "PONG";
203 config.quit = NULL;
204 config.server_port = DEFAULT_CLAMD_PORT;
187 } 205 }
188 /* fallthrough check, so it's supposed to use reverse matching */ 206 /* fallthrough check, so it's supposed to use reverse matching */
189 else if (strcmp(SERVICE, "TCP")) 207 else if (strcmp(config.service, "TCP")) {
190 usage(_("CRITICAL - Generic check_tcp called with unknown service\n")); 208 usage(_("CRITICAL - Generic check_tcp called with unknown service\n"));
191 209 }
192 server_address = "127.0.0.1";
193 server_port = PORT;
194 server_send = SEND;
195 server_quit = QUIT;
196 char *status = NULL;
197 210
198 /* Parse extra opts if any */ 211 /* Parse extra opts if any */
199 argv = np_extra_opts(&argc, argv, progname); 212 argv = np_extra_opts(&argc, argv, progname);
200 213
201 if (process_arguments(argc, argv) == ERROR) 214 check_tcp_config_wrapper paw = process_arguments(argc, argv, config);
215 if (paw.errorcode == ERROR) {
202 usage4(_("Could not parse arguments")); 216 usage4(_("Could not parse arguments"));
217 }
218
219 config = paw.config;
203 220
204 if (flags & FLAG_VERBOSE) { 221 if (verbosity > 0) {
205 printf("Using service %s\n", SERVICE); 222 printf("Using service %s\n", config.service);
206 printf("Port: %d\n", server_port); 223 printf("Port: %d\n", config.server_port);
207 printf("flags: 0x%x\n", (int)flags);
208 } 224 }
209 225
210 if (EXPECT && !server_expect_count) 226 if ((config.server_expect_count == 0) && config.server_expect[0]) {
211 server_expect_count++; 227 config.server_expect_count++;
228 }
212 229
213 if (PROTOCOL == IPPROTO_UDP && !(server_expect_count && server_send)) { 230 if (config.protocol == IPPROTO_UDP && !(config.server_expect_count && config.send)) {
214 usage(_("With UDP checks, a send/expect string must be specified.")); 231 usage(_("With UDP checks, a send/expect string must be specified."));
215 } 232 }
216 233
234 // Initialize check stuff before setting timers
235 mp_check overall = mp_check_init();
236 if (config.output_format_set) {
237 mp_set_format(config.output_format);
238 }
239
217 /* set up the timer */ 240 /* set up the timer */
218 signal(SIGALRM, socket_timeout_alarm_handler); 241 signal(SIGALRM, socket_timeout_alarm_handler);
219 alarm(socket_timeout); 242 alarm(socket_timeout);
220 243
221 /* try to connect to the host at the given port number */ 244 /* try to connect to the host at the given port number */
222 struct timeval tv; 245 struct timeval start_time;
223 gettimeofday(&tv, NULL); 246 gettimeofday(&start_time, NULL);
224 247
225 int result = STATE_UNKNOWN; 248 int socket_descriptor = 0;
226 result = np_net_connect(server_address, server_port, &sd, PROTOCOL); 249 mp_subcheck inital_connect_result = mp_subcheck_init();
227 if (result == STATE_CRITICAL) 250
228 return econn_refuse_state; 251 // Try initial connection
252 if (np_net_connect(config.server_address, config.server_port, &socket_descriptor,
253 config.protocol) == STATE_CRITICAL) {
254 // Early exit here, we got connection refused
255 inital_connect_result =
256 mp_set_subcheck_state(inital_connect_result, config.econn_refuse_state);
257 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was REFUSED",
258 config.server_address, config.server_port);
259 mp_add_subcheck_to_check(&overall, inital_connect_result);
260 mp_exit(overall);
261 } else {
262 inital_connect_result = mp_set_subcheck_state(inital_connect_result, STATE_OK);
263 xasprintf(&inital_connect_result.output, "Connection to %s on port %i was a SUCCESS",
264 config.server_address, config.server_port);
265 mp_add_subcheck_to_check(&overall, inital_connect_result);
266 }
229 267
230#ifdef HAVE_SSL 268#ifdef HAVE_SSL
231 if (flags & FLAG_SSL) { 269 if (config.use_tls) {
232 result = np_net_ssl_init_with_hostname(sd, (sni_specified ? sni : NULL)); 270 mp_subcheck tls_connection_result = mp_subcheck_init();
233 if (result == STATE_OK && check_cert) { 271 mp_state_enum result = np_net_ssl_init_with_hostname(
234 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 272 socket_descriptor, (config.sni_specified ? config.sni : NULL));
273 tls_connection_result = mp_set_subcheck_default_state(tls_connection_result, result);
274
275 if (result == STATE_OK) {
276 xasprintf(&tls_connection_result.output, "TLS connection succeeded");
277
278 if (config.check_cert) {
279 result =
280 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
281
282 mp_subcheck tls_certificate_lifetime_result = mp_subcheck_init();
283 tls_certificate_lifetime_result =
284 mp_set_subcheck_state(tls_certificate_lifetime_result, result);
285
286 if (result == STATE_OK) {
287 xasprintf(&tls_certificate_lifetime_result.output,
288 "Certificate lifetime is within thresholds");
289 } else if (result == STATE_WARNING) {
290 xasprintf(&tls_certificate_lifetime_result.output,
291 "Certificate lifetime is violating warning threshold (%i)",
292 config.days_till_exp_warn);
293 } else if (result == STATE_CRITICAL) {
294 xasprintf(&tls_certificate_lifetime_result.output,
295 "Certificate lifetime is violating critical threshold (%i)",
296 config.days_till_exp_crit);
297 } else {
298 xasprintf(&tls_certificate_lifetime_result.output,
299 "Certificate lifetime is somehow unknown");
300 }
301
302 mp_add_subcheck_to_subcheck(&tls_connection_result,
303 tls_certificate_lifetime_result);
304 }
305
306 mp_add_subcheck_to_check(&overall, tls_connection_result);
307 } else {
308 xasprintf(&tls_connection_result.output, "TLS connection failed");
309 mp_add_subcheck_to_check(&overall, tls_connection_result);
310
311 if (socket_descriptor) {
312 close(socket_descriptor);
313 }
314 np_net_ssl_cleanup();
315
316 mp_exit(overall);
235 } 317 }
236 } 318 }
237 if (result != STATE_OK) {
238 if (sd)
239 close(sd);
240 np_net_ssl_cleanup();
241 return result;
242 }
243#endif /* HAVE_SSL */ 319#endif /* HAVE_SSL */
244 320
245 if (server_send != NULL) { /* Something to send? */ 321 if (config.send != NULL) { /* Something to send? */
246 my_send(server_send, strlen(server_send)); 322 my_send(socket_descriptor, config.send, strlen(config.send), config.use_tls);
247 } 323 }
248 324
249 if (delay > 0) { 325 if (config.delay > 0) {
250 tv.tv_sec += delay; 326 start_time.tv_sec += config.delay;
251 sleep(delay); 327 sleep(config.delay);
252 } 328 }
253 329
254 if (flags & FLAG_VERBOSE) { 330 if (verbosity > 0) {
255 if (server_send) { 331 if (config.send) {
256 printf("Send string: %s\n", server_send); 332 printf("Send string: %s\n", config.send);
333 }
334 if (config.quit) {
335 printf("Quit string: %s\n", config.quit);
257 } 336 }
258 if (server_quit) { 337 printf("server_expect_count: %d\n", (int)config.server_expect_count);
259 printf("Quit string: %s\n", server_quit); 338 for (size_t i = 0; i < config.server_expect_count; i++) {
339 printf("\t%zd: %s\n", i, config.server_expect[i]);
260 } 340 }
261 printf("server_expect_count: %d\n", (int)server_expect_count);
262 for (size_t i = 0; i < server_expect_count; i++)
263 printf("\t%zd: %s\n", i, server_expect[i]);
264 } 341 }
265 342
266 /* if(len) later on, we know we have a non-NULL response */ 343 /* if(len) later on, we know we have a non-NULL response */
267 ssize_t len = 0; 344 ssize_t len = 0;
345 char *received_buffer = NULL;
346 enum np_match_result match = NP_MATCH_NONE;
347 mp_subcheck expected_data_result = mp_subcheck_init();
268 348
269 int match = -1; 349 if (config.server_expect_count) {
270 struct timeval timeout;
271 fd_set rfds;
272 FD_ZERO(&rfds);
273 if (server_expect_count) {
274 ssize_t received = 0; 350 ssize_t received = 0;
351 char buffer[MAXBUF];
275 352
276 /* watch for the expect string */ 353 /* watch for the expect string */
277 while ((received = my_recv(buffer, sizeof(buffer))) > 0) { 354 while ((received = my_recv(socket_descriptor, buffer, sizeof(buffer), config.use_tls)) >
278 status = realloc(status, len + received + 1); 355 0) {
279 memcpy(&status[len], buffer, received); 356 received_buffer = realloc(received_buffer, len + received + 1);
357
358 if (received_buffer == NULL) {
359 die(STATE_UNKNOWN, _("Allocation failed"));
360 }
361
362 memcpy(&received_buffer[len], buffer, received);
280 len += received; 363 len += received;
281 status[len] = '\0'; 364 received_buffer[len] = '\0';
282 365
283 /* stop reading if user-forced */ 366 /* stop reading if user-forced */
284 if (maxbytes && len >= maxbytes) 367 if (config.maxbytes && len >= config.maxbytes) {
285 break; 368 break;
369 }
286 370
287 if ((match = np_expect_match(status, server_expect, server_expect_count, match_flags)) != NP_MATCH_RETRY) 371 if ((match = np_expect_match(received_buffer, config.server_expect,
372 config.server_expect_count, config.match_flags)) !=
373 NP_MATCH_RETRY) {
288 break; 374 break;
375 }
376
377 fd_set rfds;
378 FD_ZERO(&rfds);
379 FD_SET(socket_descriptor, &rfds);
289 380
290 /* some protocols wait for further input, so make sure we don't wait forever */ 381 /* some protocols wait for further input, so make sure we don't wait forever */
291 FD_SET(sd, &rfds); 382 struct timeval timeout;
292 timeout.tv_sec = READ_TIMEOUT; 383 timeout.tv_sec = READ_TIMEOUT;
293 timeout.tv_usec = 0; 384 timeout.tv_usec = 0;
294 if (select(sd + 1, &rfds, NULL, NULL, &timeout) <= 0) 385
386 if (select(socket_descriptor + 1, &rfds, NULL, NULL, &timeout) <= 0) {
295 break; 387 break;
388 }
296 } 389 }
297 390
298 if (match == NP_MATCH_RETRY) 391 if (match == NP_MATCH_RETRY) {
299 match = NP_MATCH_FAILURE; 392 match = NP_MATCH_FAILURE;
393 }
300 394
301 /* no data when expected, so return critical */ 395 /* no data when expected, so return critical */
302 if (len == 0) 396 if (len == 0) {
303 die(STATE_CRITICAL, _("No data received from host\n")); 397 xasprintf(&expected_data_result.output, "Received no data when some was expected");
398 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_CRITICAL);
399 mp_add_subcheck_to_check(&overall, expected_data_result);
400 mp_exit(overall);
401 }
304 402
305 /* print raw output if we're debugging */ 403 /* print raw output if we're debugging */
306 if (flags & FLAG_VERBOSE) 404 if (verbosity > 0) {
307 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n", (int)len + 1, status); 405 printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n",
406 (int)len + 1, received_buffer);
407 }
308 /* strip whitespace from end of output */ 408 /* strip whitespace from end of output */
309 while (--len > 0 && isspace(status[len])) 409 while (--len > 0 && isspace(received_buffer[len])) {
310 status[len] = '\0'; 410 received_buffer[len] = '\0';
411 }
311 } 412 }
312 413
313 if (server_quit != NULL) { 414 if (config.quit != NULL) {
314 my_send(server_quit, strlen(server_quit)); 415 my_send(socket_descriptor, config.quit, strlen(config.quit), config.use_tls);
416 }
417
418 if (socket_descriptor) {
419 close(socket_descriptor);
315 } 420 }
316 if (sd)
317 close(sd);
318#ifdef HAVE_SSL 421#ifdef HAVE_SSL
319 np_net_ssl_cleanup(); 422 np_net_ssl_cleanup();
320#endif 423#endif
321 424
322 microsec = deltime(tv); 425 long microsec = deltime(start_time);
323 elapsed_time = (double)microsec / 1.0e6; 426 double elapsed_time = (double)microsec / 1.0e6;
427
428 mp_subcheck elapsed_time_result = mp_subcheck_init();
429
430 mp_perfdata time_pd = perfdata_init();
431 time_pd = mp_set_pd_value(time_pd, elapsed_time);
432 time_pd.label = "time";
433 time_pd.uom = "s";
434
435 if (config.critical_time_set && elapsed_time > config.critical_time) {
436 xasprintf(&elapsed_time_result.output,
437 "Connection time %fs exceeded critical threshold (%f)", elapsed_time,
438 config.critical_time);
439
440 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_CRITICAL);
441 time_pd.crit_present = true;
442 mp_range crit_val = mp_range_init();
443
444 crit_val.end = mp_create_pd_value(config.critical_time);
445 crit_val.end_infinity = false;
446
447 time_pd.crit = crit_val;
448 } else if (config.warning_time_set && elapsed_time > config.warning_time) {
449 xasprintf(&elapsed_time_result.output,
450 "Connection time %fs exceeded warning threshold (%f)", elapsed_time,
451 config.critical_time);
452
453 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_WARNING);
454 time_pd.warn_present = true;
455 mp_range warn_val = mp_range_init();
456 warn_val.end = mp_create_pd_value(config.critical_time);
457 warn_val.end_infinity = false;
458
459 time_pd.warn = warn_val;
460 } else {
461 elapsed_time_result = mp_set_subcheck_state(elapsed_time_result, STATE_OK);
462 xasprintf(&elapsed_time_result.output, "Connection time %fs is within thresholds",
463 elapsed_time);
464 }
324 465
325 if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time) 466 mp_add_perfdata_to_subcheck(&elapsed_time_result, time_pd);
326 result = STATE_CRITICAL; 467 mp_add_subcheck_to_check(&overall, elapsed_time_result);
327 else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time)
328 result = STATE_WARNING;
329 468
330 /* did we get the response we hoped? */ 469 /* did we get the response we hoped? */
331 if (match == NP_MATCH_FAILURE && result != STATE_CRITICAL) 470 if (match == NP_MATCH_FAILURE) {
332 result = expect_mismatch_state; 471 expected_data_result =
472 mp_set_subcheck_state(expected_data_result, config.expect_mismatch_state);
473 xasprintf(&expected_data_result.output, "Answer failed to match expectation");
474 mp_add_subcheck_to_check(&overall, expected_data_result);
475 } else if (match == NP_MATCH_SUCCESS) {
476 expected_data_result = mp_set_subcheck_state(expected_data_result, STATE_OK);
477 xasprintf(&expected_data_result.output, "The answer of the server matched the expectation");
478 mp_add_subcheck_to_check(&overall, expected_data_result);
479 }
333 480
334 /* reset the alarm */ 481 /* reset the alarm */
335 alarm(0); 482 alarm(0);
336 483
337 /* this is a bit stupid, because we don't want to print the 484 mp_exit(overall);
338 * response time (which can look ok to the user) if we didn't get
339 * the response we were looking for. if-else */
340 printf("%s %s - ", SERVICE, state_text(result));
341
342 if (match == NP_MATCH_FAILURE && len && !(flags & FLAG_HIDE_OUTPUT))
343 printf("Unexpected response from host/socket: %s", status);
344 else {
345 if (match == NP_MATCH_FAILURE)
346 printf("Unexpected response from host/socket on ");
347 else
348 printf("%.3f second response time on ", elapsed_time);
349 if (server_address[0] != '/') {
350 if (host_specified)
351 printf("%s port %d", server_address, server_port);
352 else
353 printf("port %d", server_port);
354 } else
355 printf("socket %s", server_address);
356 }
357
358 if (match != NP_MATCH_FAILURE && !(flags & FLAG_HIDE_OUTPUT) && len)
359 printf(" [%s]", status);
360
361 /* perf-data doesn't apply when server doesn't talk properly,
362 * so print all zeroes on warn and crit. Use fperfdata since
363 * localisation settings can make different outputs */
364 if (match == NP_MATCH_FAILURE)
365 printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), 0,
366 (flags & FLAG_TIME_CRIT ? true : false), 0, true, 0, true, socket_timeout));
367 else
368 printf("|%s", fperfdata("time", elapsed_time, "s", (flags & FLAG_TIME_WARN ? true : false), warning_time,
369 (flags & FLAG_TIME_CRIT ? true : false), critical_time, true, 0, true, socket_timeout));
370
371 putchar('\n');
372 return result;
373} 485}
374 486
375/* process command-line arguments */ 487/* process command-line arguments */
376static int process_arguments(int argc, char **argv) { 488static check_tcp_config_wrapper process_arguments(int argc, char **argv, check_tcp_config config) {
377 enum { 489 enum {
378 SNI_OPTION = CHAR_MAX + 1 490 SNI_OPTION = CHAR_MAX + 1,
491 output_format_index,
379 }; 492 };
380 493
381 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 494 static struct option longopts[] = {
382 {"critical", required_argument, 0, 'c'}, 495 {"hostname", required_argument, 0, 'H'},
383 {"warning", required_argument, 0, 'w'}, 496 {"critical", required_argument, 0, 'c'},
384 {"critical-codes", required_argument, 0, 'C'}, 497 {"warning", required_argument, 0, 'w'},
385 {"warning-codes", required_argument, 0, 'W'}, 498 {"critical-codes", required_argument, 0, 'C'},
386 {"timeout", required_argument, 0, 't'}, 499 {"warning-codes", required_argument, 0, 'W'},
387 {"protocol", required_argument, 0, 'P'}, /* FIXME: Unhandled */ 500 {"timeout", required_argument, 0, 't'},
388 {"port", required_argument, 0, 'p'}, 501 {"protocol", required_argument, 0, 'P'}, /* FIXME: Unhandled */
389 {"escape", no_argument, 0, 'E'}, 502 {"port", required_argument, 0, 'p'},
390 {"all", no_argument, 0, 'A'}, 503 {"escape", no_argument, 0, 'E'},
391 {"send", required_argument, 0, 's'}, 504 {"all", no_argument, 0, 'A'},
392 {"expect", required_argument, 0, 'e'}, 505 {"send", required_argument, 0, 's'},
393 {"maxbytes", required_argument, 0, 'm'}, 506 {"expect", required_argument, 0, 'e'},
394 {"quit", required_argument, 0, 'q'}, 507 {"maxbytes", required_argument, 0, 'm'},
395 {"jail", no_argument, 0, 'j'}, 508 {"quit", required_argument, 0, 'q'},
396 {"delay", required_argument, 0, 'd'}, 509 {"jail", no_argument, 0, 'j'},
397 {"refuse", required_argument, 0, 'r'}, 510 {"delay", required_argument, 0, 'd'},
398 {"mismatch", required_argument, 0, 'M'}, 511 {"refuse", required_argument, 0, 'r'},
399 {"use-ipv4", no_argument, 0, '4'}, 512 {"mismatch", required_argument, 0, 'M'},
400 {"use-ipv6", no_argument, 0, '6'}, 513 {"use-ipv4", no_argument, 0, '4'},
401 {"verbose", no_argument, 0, 'v'}, 514 {"use-ipv6", no_argument, 0, '6'},
402 {"version", no_argument, 0, 'V'}, 515 {"verbose", no_argument, 0, 'v'},
403 {"help", no_argument, 0, 'h'}, 516 {"version", no_argument, 0, 'V'},
404 {"ssl", no_argument, 0, 'S'}, 517 {"help", no_argument, 0, 'h'},
405 {"sni", required_argument, 0, SNI_OPTION}, 518 {"ssl", no_argument, 0, 'S'},
406 {"certificate", required_argument, 0, 'D'}, 519 {"sni", required_argument, 0, SNI_OPTION},
407 {0, 0, 0, 0}}; 520 {"certificate", required_argument, 0, 'D'},
408 521 {"output-format", required_argument, 0, output_format_index},
409 if (argc < 2) 522 {0, 0, 0, 0}};
523
524 if (argc < 2) {
410 usage4(_("No arguments found")); 525 usage4(_("No arguments found"));
526 }
411 527
412 /* backwards compatibility */ 528 /* backwards compatibility */
413 for (int i = 1; i < argc; i++) { 529 for (int i = 1; i < argc; i++) {
414 if (strcmp("-to", argv[i]) == 0) 530 if (strcmp("-to", argv[i]) == 0) {
415 strcpy(argv[i], "-t"); 531 strcpy(argv[i], "-t");
416 else if (strcmp("-wt", argv[i]) == 0) 532 } else if (strcmp("-wt", argv[i]) == 0) {
417 strcpy(argv[i], "-w"); 533 strcpy(argv[i], "-w");
418 else if (strcmp("-ct", argv[i]) == 0) 534 } else if (strcmp("-ct", argv[i]) == 0) {
419 strcpy(argv[i], "-c"); 535 strcpy(argv[i], "-c");
536 }
420 } 537 }
421 538
422 if (!is_option(argv[1])) { 539 if (!is_option(argv[1])) {
423 server_address = argv[1]; 540 config.server_address = argv[1];
424 argv[1] = argv[0]; 541 argv[1] = argv[0];
425 argv = &argv[1]; 542 argv = &argv[1];
426 argc--; 543 argc--;
427 } 544 }
428 545
429 int option_char;
430 bool escape = false; 546 bool escape = false;
547
431 while (true) { 548 while (true) {
432 int option = 0; 549 int option = 0;
433 option_char = getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option); 550 int option_index =
551 getopt_long(argc, argv, "+hVv46EAH:s:e:q:m:c:w:t:p:C:W:d:Sr:jD:M:", longopts, &option);
434 552
435 if (option_char == -1 || option_char == EOF || option_char == 1) 553 if (option_index == -1 || option_index == EOF || option_index == 1) {
436 break; 554 break;
555 }
437 556
438 switch (option_char) { 557 switch (option_index) {
439 case '?': /* print short usage statement if args not parsable */ 558 case '?': /* print short usage statement if args not parsable */
440 usage5(); 559 usage5();
441 case 'h': /* help */ 560 case 'h': /* help */
442 print_help(); 561 print_help(config.service);
443 exit(STATE_UNKNOWN); 562 exit(STATE_UNKNOWN);
444 case 'V': /* version */ 563 case 'V': /* version */
445 print_revision(progname, NP_VERSION); 564 print_revision(progname, NP_VERSION);
446 exit(STATE_UNKNOWN); 565 exit(STATE_UNKNOWN);
447 case 'v': /* verbose mode */ 566 case 'v': /* verbose mode */
448 flags |= FLAG_VERBOSE; 567 verbosity++;
449 match_flags |= NP_MATCH_VERBOSE; 568 config.match_flags |= NP_MATCH_VERBOSE;
450 break; 569 break;
451 case '4': 570 case '4': // Apparently unused TODO
452 address_family = AF_INET; 571 address_family = AF_INET;
453 break; 572 break;
454 case '6': 573 case '6': // Apparently unused TODO
455#ifdef USE_IPV6 574#ifdef USE_IPV6
456 address_family = AF_INET6; 575 address_family = AF_INET6;
457#else 576#else
@@ -459,163 +578,192 @@ static int process_arguments(int argc, char **argv) {
459#endif 578#endif
460 break; 579 break;
461 case 'H': /* hostname */ 580 case 'H': /* hostname */
462 host_specified = true; 581 config.host_specified = true;
463 server_address = optarg; 582 config.server_address = optarg;
464 break; 583 break;
465 case 'c': /* critical */ 584 case 'c': /* critical */
466 critical_time = strtod(optarg, NULL); 585 config.critical_time = strtod(optarg, NULL);
467 flags |= FLAG_TIME_CRIT; 586 config.critical_time_set = true;
468 break; 587 break;
469 case 'j': /* hide output */ 588 case 'j': /* hide output */
470 flags |= FLAG_HIDE_OUTPUT; 589 config.hide_output = true;
471 break; 590 break;
472 case 'w': /* warning */ 591 case 'w': /* warning */
473 warning_time = strtod(optarg, NULL); 592 config.warning_time = strtod(optarg, NULL);
474 flags |= FLAG_TIME_WARN; 593 config.warning_time_set = true;
475 break;
476 case 'C':
477 crit_codes = realloc(crit_codes, ++crit_codes_count);
478 crit_codes[crit_codes_count - 1] = optarg;
479 break;
480 case 'W':
481 warn_codes = realloc(warn_codes, ++warn_codes_count);
482 warn_codes[warn_codes_count - 1] = optarg;
483 break; 594 break;
484 case 't': /* timeout */ 595 case 't': /* timeout */
485 if (!is_intpos(optarg)) 596 if (!is_intpos(optarg)) {
486 usage4(_("Timeout interval must be a positive integer")); 597 usage4(_("Timeout interval must be a positive integer"));
487 else 598 } else {
488 socket_timeout = atoi(optarg); 599 socket_timeout = atoi(optarg);
600 }
489 break; 601 break;
490 case 'p': /* port */ 602 case 'p': /* port */
491 if (!is_intpos(optarg)) 603 if (!is_intpos(optarg)) {
492 usage4(_("Port must be a positive integer")); 604 usage4(_("Port must be a positive integer"));
493 else 605 } else {
494 server_port = atoi(optarg); 606 config.server_port = atoi(optarg);
607 }
495 break; 608 break;
496 case 'E': 609 case 'E':
497 escape = true; 610 escape = true;
498 break; 611 break;
499 case 's': 612 case 's':
500 if (escape) 613 if (escape) {
501 server_send = np_escaped_string(optarg); 614 config.send = np_escaped_string(optarg);
502 else 615 } else {
503 xasprintf(&server_send, "%s", optarg); 616 xasprintf(&config.send, "%s", optarg);
617 }
504 break; 618 break;
505 case 'e': /* expect string (may be repeated) */ 619 case 'e': /* expect string (may be repeated) */
506 match_flags &= ~NP_MATCH_EXACT; 620 config.match_flags &= ~NP_MATCH_EXACT;
507 if (server_expect_count == 0) 621 if (config.server_expect_count == 0) {
508 server_expect = malloc(sizeof(char *) * (++server_expect_count)); 622 config.server_expect = malloc(sizeof(char *) * (++config.server_expect_count));
509 else 623 } else {
510 server_expect = realloc(server_expect, sizeof(char *) * (++server_expect_count)); 624 config.server_expect =
511 server_expect[server_expect_count - 1] = optarg; 625 realloc(config.server_expect, sizeof(char *) * (++config.server_expect_count));
626 }
627
628 if (config.server_expect == NULL) {
629 die(STATE_UNKNOWN, _("Allocation failed"));
630 }
631 config.server_expect[config.server_expect_count - 1] = optarg;
512 break; 632 break;
513 case 'm': 633 case 'm':
514 if (!is_intpos(optarg)) 634 if (!is_intpos(optarg)) {
515 usage4(_("Maxbytes must be a positive integer")); 635 usage4(_("Maxbytes must be a positive integer"));
516 else 636 } else {
517 maxbytes = strtol(optarg, NULL, 0); 637 config.maxbytes = strtol(optarg, NULL, 0);
638 }
518 break; 639 break;
519 case 'q': 640 case 'q':
520 if (escape) 641 if (escape) {
521 server_quit = np_escaped_string(optarg); 642 config.quit = np_escaped_string(optarg);
522 else 643 } else {
523 xasprintf(&server_quit, "%s\r\n", optarg); 644 xasprintf(&config.quit, "%s\r\n", optarg);
645 }
524 break; 646 break;
525 case 'r': 647 case 'r':
526 if (!strncmp(optarg, "ok", 2)) 648 if (!strncmp(optarg, "ok", 2)) {
527 econn_refuse_state = STATE_OK; 649 config.econn_refuse_state = STATE_OK;
528 else if (!strncmp(optarg, "warn", 4)) 650 } else if (!strncmp(optarg, "warn", 4)) {
529 econn_refuse_state = STATE_WARNING; 651 config.econn_refuse_state = STATE_WARNING;
530 else if (!strncmp(optarg, "crit", 4)) 652 } else if (!strncmp(optarg, "crit", 4)) {
531 econn_refuse_state = STATE_CRITICAL; 653 config.econn_refuse_state = STATE_CRITICAL;
532 else 654 } else {
533 usage4(_("Refuse must be one of ok, warn, crit")); 655 usage4(_("Refuse must be one of ok, warn, crit"));
656 }
534 break; 657 break;
535 case 'M': 658 case 'M':
536 if (!strncmp(optarg, "ok", 2)) 659 if (!strncmp(optarg, "ok", 2)) {
537 expect_mismatch_state = STATE_OK; 660 config.expect_mismatch_state = STATE_OK;
538 else if (!strncmp(optarg, "warn", 4)) 661 } else if (!strncmp(optarg, "warn", 4)) {
539 expect_mismatch_state = STATE_WARNING; 662 config.expect_mismatch_state = STATE_WARNING;
540 else if (!strncmp(optarg, "crit", 4)) 663 } else if (!strncmp(optarg, "crit", 4)) {
541 expect_mismatch_state = STATE_CRITICAL; 664 config.expect_mismatch_state = STATE_CRITICAL;
542 else 665 } else {
543 usage4(_("Mismatch must be one of ok, warn, crit")); 666 usage4(_("Mismatch must be one of ok, warn, crit"));
667 }
544 break; 668 break;
545 case 'd': 669 case 'd':
546 if (is_intpos(optarg)) 670 if (is_intpos(optarg)) {
547 delay = atoi(optarg); 671 config.delay = atoi(optarg);
548 else 672 } else {
549 usage4(_("Delay must be a positive integer")); 673 usage4(_("Delay must be a positive integer"));
674 }
550 break; 675 break;
551 case 'D': { /* Check SSL cert validity - days 'til certificate expiration */ 676 case 'D': /* Check SSL cert validity - days 'til certificate expiration */
552#ifdef HAVE_SSL 677#ifdef HAVE_SSL
553# ifdef USE_OPENSSL /* XXX */ 678# ifdef USE_OPENSSL /* XXX */
679 {
554 char *temp; 680 char *temp;
555 if ((temp = strchr(optarg, ',')) != NULL) { 681 if ((temp = strchr(optarg, ',')) != NULL) {
556 *temp = '\0'; 682 *temp = '\0';
557 if (!is_intnonneg(optarg)) 683 if (!is_intnonneg(optarg)) {
558 usage2(_("Invalid certificate expiration period"), optarg); 684 usage2(_("Invalid certificate expiration period"), optarg);
559 days_till_exp_warn = atoi(optarg); 685 }
686 config.days_till_exp_warn = atoi(optarg);
560 *temp = ','; 687 *temp = ',';
561 temp++; 688 temp++;
562 if (!is_intnonneg(temp)) 689 if (!is_intnonneg(temp)) {
563 usage2(_("Invalid certificate expiration period"), temp); 690 usage2(_("Invalid certificate expiration period"), temp);
564 days_till_exp_crit = atoi(temp); 691 }
692 config.days_till_exp_crit = atoi(temp);
565 } else { 693 } else {
566 days_till_exp_crit = 0; 694 config.days_till_exp_crit = 0;
567 if (!is_intnonneg(optarg)) 695 if (!is_intnonneg(optarg)) {
568 usage2(_("Invalid certificate expiration period"), optarg); 696 usage2(_("Invalid certificate expiration period"), optarg);
569 days_till_exp_warn = atoi(optarg); 697 }
698 config.days_till_exp_warn = atoi(optarg);
570 } 699 }
571 check_cert = true; 700 config.check_cert = true;
572 flags |= FLAG_SSL; 701 config.use_tls = true;
573 } break; 702 } break;
574# endif /* USE_OPENSSL */ 703# endif /* USE_OPENSSL */
575#endif 704#endif
576 /* fallthrough if we don't have ssl */ 705 /* fallthrough if we don't have ssl */
577 case 'S': 706 case 'S':
578#ifdef HAVE_SSL 707#ifdef HAVE_SSL
579 flags |= FLAG_SSL; 708 config.use_tls = true;
580#else 709#else
581 die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); 710 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
582#endif 711#endif
583 break; 712 break;
584 case SNI_OPTION: 713 case SNI_OPTION:
585#ifdef HAVE_SSL 714#ifdef HAVE_SSL
586 flags |= FLAG_SSL; 715 config.use_tls = true;
587 sni_specified = true; 716 config.sni_specified = true;
588 sni = optarg; 717 config.sni = optarg;
589#else 718#else
590 die(STATE_UNKNOWN, _("Invalid option - SSL is not available")); 719 die(STATE_UNKNOWN, _("Invalid option - SSL is not available"));
591#endif 720#endif
592 break; 721 break;
593 case 'A': 722 case 'A':
594 match_flags |= NP_MATCH_ALL; 723 config.match_flags |= NP_MATCH_ALL;
724 break;
725 case output_format_index: {
726 parsed_output_format parser = mp_parse_output_format(optarg);
727 if (!parser.parsing_success) {
728 // TODO List all available formats here, maybe add anothoer usage function
729 printf("Invalid output format: %s\n", optarg);
730 exit(STATE_UNKNOWN);
731 }
732
733 config.output_format_set = true;
734 config.output_format = parser.output_format;
595 break; 735 break;
596 } 736 }
737 }
597 } 738 }
598 739
599 option_char = optind; 740 int index = optind;
600 if (!host_specified && option_char < argc) 741 if (!config.host_specified && index < argc) {
601 server_address = strdup(argv[option_char++]); 742 config.server_address = strdup(argv[index++]);
743 }
602 744
603 if (server_address == NULL) 745 if (config.server_address == NULL) {
604 usage4(_("You must provide a server address")); 746 usage4(_("You must provide a server address"));
605 else if (server_address[0] != '/' && !is_host(server_address)) 747 } else if (config.server_address[0] != '/' && !is_host(config.server_address)) {
606 die(STATE_CRITICAL, "%s %s - %s: %s\n", SERVICE, state_text(STATE_CRITICAL), _("Invalid hostname, address or socket"), 748 die(STATE_CRITICAL, "%s %s - %s: %s\n", config.service, state_text(STATE_CRITICAL),
607 server_address); 749 _("Invalid hostname, address or socket"), config.server_address);
750 }
608 751
609 return OK; 752 check_tcp_config_wrapper result = {
753 .config = config,
754 .errorcode = OK,
755 };
756 return result;
610} 757}
611 758
612void print_help(void) { 759void print_help(const char *service) {
613 print_revision(progname, NP_VERSION); 760 print_revision(progname, NP_VERSION);
614 761
615 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 762 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
616 printf(COPYRIGHT, copyright, email); 763 printf(COPYRIGHT, copyright, email);
617 764
618 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"), SERVICE); 765 printf(_("This plugin tests %s connections with the specified host (or unix socket).\n\n"),
766 service);
619 767
620 print_usage(); 768 print_usage();
621 769
@@ -627,7 +775,8 @@ void print_help(void) {
627 printf(UT_IPv46); 775 printf(UT_IPv46);
628 776
629 printf(" %s\n", "-E, --escape"); 777 printf(" %s\n", "-E, --escape");
630 printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before send or quit option")); 778 printf(" %s\n", _("Can use \\n, \\r, \\t or \\\\ in send or quit string. Must come before "
779 "send or quit option"));
631 printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit")); 780 printf(" %s\n", _("Default: nothing added to send, \\r\\n added to end of quit"));
632 printf(" %s\n", "-s, --send=STRING"); 781 printf(" %s\n", "-s, --send=STRING");
633 printf(" %s\n", _("String to send to the server")); 782 printf(" %s\n", _("String to send to the server"));
@@ -640,7 +789,8 @@ void print_help(void) {
640 printf(" %s\n", "-r, --refuse=ok|warn|crit"); 789 printf(" %s\n", "-r, --refuse=ok|warn|crit");
641 printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)")); 790 printf(" %s\n", _("Accept TCP refusals with states ok, warn, crit (default: crit)"));
642 printf(" %s\n", "-M, --mismatch=ok|warn|crit"); 791 printf(" %s\n", "-M, --mismatch=ok|warn|crit");
643 printf(" %s\n", _("Accept expected string mismatches with states ok, warn, crit (default: warn)")); 792 printf(" %s\n",
793 _("Accept expected string mismatches with states ok, warn, crit (default: warn)"));
644 printf(" %s\n", "-j, --jail"); 794 printf(" %s\n", "-j, --jail");
645 printf(" %s\n", _("Hide output from TCP socket")); 795 printf(" %s\n", _("Hide output from TCP socket"));
646 printf(" %s\n", "-m, --maxbytes=INTEGER"); 796 printf(" %s\n", "-m, --maxbytes=INTEGER");
@@ -662,6 +812,7 @@ void print_help(void) {
662 812
663 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 813 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
664 814
815 printf(UT_OUTPUT_FORMAT);
665 printf(UT_VERBOSE); 816 printf(UT_VERBOSE);
666 817
667 printf(UT_SUPPORT); 818 printf(UT_SUPPORT);
@@ -669,7 +820,8 @@ void print_help(void) {
669 820
670void print_usage(void) { 821void print_usage(void) {
671 printf("%s\n", _("Usage:")); 822 printf("%s\n", _("Usage:"));
672 printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n", progname); 823 printf("%s -H host -p port [-w <warning time>] [-c <critical time>] [-s <send string>]\n",
824 progname);
673 printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n"); 825 printf("[-e <expect string>] [-q <quit string>][-m <maximum bytes>] [-d <delay>]\n");
674 printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n"); 826 printf("[-t <timeout seconds>] [-r <refuse state>] [-M <mismatch state>] [-v] [-4|-6] [-j]\n");
675 printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n"); 827 printf("[-D <warn days cert expire>[,<crit days cert expire>]] [-S <use SSL>] [-E]\n");
diff --git a/plugins/check_tcp.d/config.h b/plugins/check_tcp.d/config.h
new file mode 100644
index 00000000..dc25d79e
--- /dev/null
+++ b/plugins/check_tcp.d/config.h
@@ -0,0 +1,84 @@
1#pragma once
2
3#include "../../lib/utils_tcp.h"
4#include "output.h"
5#include "states.h"
6#include <netinet/in.h>
7
8typedef struct {
9 char *server_address;
10 bool host_specified;
11 int server_port; // TODO can this be a uint16?
12
13 int protocol; /* most common is default */
14 char *service;
15 char *send;
16 char *quit;
17 char **server_expect;
18 size_t server_expect_count;
19 bool use_tls;
20#ifdef HAVE_SSL
21 char *sni;
22 bool sni_specified;
23 bool check_cert;
24 int days_till_exp_warn;
25 int days_till_exp_crit;
26#endif // HAVE_SSL
27 int match_flags;
28 mp_state_enum expect_mismatch_state;
29 unsigned int delay;
30
31 bool warning_time_set;
32 double warning_time;
33 bool critical_time_set;
34 double critical_time;
35
36 mp_state_enum econn_refuse_state;
37
38 ssize_t maxbytes;
39
40 bool hide_output;
41
42 bool output_format_set;
43 mp_output_format output_format;
44} check_tcp_config;
45
46check_tcp_config check_tcp_config_init() {
47 check_tcp_config result = {
48 .server_address = "127.0.0.1",
49 .host_specified = false,
50 .server_port = 0,
51
52 .protocol = IPPROTO_TCP,
53 .service = "TCP",
54 .send = NULL,
55 .quit = NULL,
56 .server_expect = NULL,
57 .server_expect_count = 0,
58 .use_tls = false,
59#ifdef HAVE_SSL
60 .sni = NULL,
61 .sni_specified = false,
62 .check_cert = false,
63 .days_till_exp_warn = 0,
64 .days_till_exp_crit = 0,
65#endif // HAVE_SSL
66 .match_flags = NP_MATCH_EXACT,
67 .expect_mismatch_state = STATE_WARNING,
68 .delay = 0,
69
70 .warning_time_set = false,
71 .warning_time = 0,
72 .critical_time_set = false,
73 .critical_time = 0,
74
75 .econn_refuse_state = STATE_CRITICAL,
76
77 .maxbytes = 0,
78
79 .hide_output = false,
80
81 .output_format_set = false,
82 };
83 return result;
84}
diff --git a/plugins/check_time.c b/plugins/check_time.c
index d1f50683..fc9ba3f9 100644
--- a/plugins/check_time.c
+++ b/plugins/check_time.c
@@ -28,6 +28,7 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "states.h"
31const char *progname = "check_time"; 32const char *progname = "check_time";
32const char *copyright = "1999-2024"; 33const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
@@ -35,28 +36,15 @@ const char *email = "devel@monitoring-plugins.org";
35#include "common.h" 36#include "common.h"
36#include "netutils.h" 37#include "netutils.h"
37#include "utils.h" 38#include "utils.h"
38 39#include "check_time.d/config.h"
39enum {
40 TIME_PORT = 37
41};
42 40
43#define UNIX_EPOCH 2208988800UL 41#define UNIX_EPOCH 2208988800UL
44 42
45static uint32_t raw_server_time; 43typedef struct {
46static unsigned long server_time, diff_time; 44 int errorcode;
47static int warning_time = 0; 45 check_time_config config;
48static bool check_warning_time = false; 46} check_time_config_wrapper;
49static int critical_time = 0; 47static check_time_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
50static bool check_critical_time = false;
51static unsigned long warning_diff = 0;
52static bool check_warning_diff = false;
53static unsigned long critical_diff = 0;
54static bool check_critical_diff = false;
55static int server_port = TIME_PORT;
56static char *server_address = NULL;
57static bool use_udp = false;
58
59static int process_arguments(int, char **);
60static void print_help(void); 48static void print_help(void);
61void print_usage(void); 49void print_usage(void);
62 50
@@ -68,8 +56,12 @@ int main(int argc, char **argv) {
68 /* Parse extra opts if any */ 56 /* Parse extra opts if any */
69 argv = np_extra_opts(&argc, argv, progname); 57 argv = np_extra_opts(&argc, argv, progname);
70 58
71 if (process_arguments(argc, argv) == ERROR) 59 check_time_config_wrapper tmp_config = process_arguments(argc, argv);
60 if (tmp_config.errorcode == ERROR) {
72 usage4(_("Could not parse arguments")); 61 usage4(_("Could not parse arguments"));
62 }
63
64 const check_time_config config = tmp_config.config;
73 65
74 /* initialize alarm signal handling */ 66 /* initialize alarm signal handling */
75 signal(SIGALRM, socket_timeout_alarm_handler); 67 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -79,37 +71,42 @@ int main(int argc, char **argv) {
79 time(&start_time); 71 time(&start_time);
80 72
81 int socket; 73 int socket;
82 int result = STATE_UNKNOWN; 74 mp_state_enum result = STATE_UNKNOWN;
83 /* try to connect to the host at the given port number */ 75 /* try to connect to the host at the given port number */
84 if (use_udp) { 76 if (config.use_udp) {
85 result = my_udp_connect(server_address, server_port, &socket); 77 result = my_udp_connect(config.server_address, config.server_port, &socket);
86 } else { 78 } else {
87 result = my_tcp_connect(server_address, server_port, &socket); 79 result = my_tcp_connect(config.server_address, config.server_port, &socket);
88 } 80 }
89 81
90 if (result != STATE_OK) { 82 if (result != STATE_OK) {
91 if (check_critical_time) 83 if (config.check_critical_time) {
92 result = STATE_CRITICAL; 84 result = STATE_CRITICAL;
93 else if (check_warning_time) 85 } else if (config.check_warning_time) {
94 result = STATE_WARNING; 86 result = STATE_WARNING;
95 else 87 } else {
96 result = STATE_UNKNOWN; 88 result = STATE_UNKNOWN;
97 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"), server_address, server_port); 89 }
90 die(result, _("TIME UNKNOWN - could not connect to server %s, port %d\n"),
91 config.server_address, config.server_port);
98 } 92 }
99 93
100 if (use_udp) { 94 if (config.use_udp) {
101 if (send(socket, "", 0, 0) < 0) { 95 if (send(socket, "", 0, 0) < 0) {
102 if (check_critical_time) 96 if (config.check_critical_time) {
103 result = STATE_CRITICAL; 97 result = STATE_CRITICAL;
104 else if (check_warning_time) 98 } else if (config.check_warning_time) {
105 result = STATE_WARNING; 99 result = STATE_WARNING;
106 else 100 } else {
107 result = STATE_UNKNOWN; 101 result = STATE_UNKNOWN;
108 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"), server_address, server_port); 102 }
103 die(result, _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"),
104 config.server_address, config.server_port);
109 } 105 }
110 } 106 }
111 107
112 /* watch for the connection string */ 108 /* watch for the connection string */
109 uint32_t raw_server_time;
113 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0); 110 result = recv(socket, (void *)&raw_server_time, sizeof(raw_server_time), 0);
114 111
115 /* close the connection */ 112 /* close the connection */
@@ -121,48 +118,59 @@ int main(int argc, char **argv) {
121 118
122 /* return a WARNING status if we couldn't read any data */ 119 /* return a WARNING status if we couldn't read any data */
123 if (result <= 0) { 120 if (result <= 0) {
124 if (check_critical_time) 121 if (config.check_critical_time) {
125 result = STATE_CRITICAL; 122 result = STATE_CRITICAL;
126 else if (check_warning_time) 123 } else if (config.check_warning_time) {
127 result = STATE_WARNING; 124 result = STATE_WARNING;
128 else 125 } else {
129 result = STATE_UNKNOWN; 126 result = STATE_UNKNOWN;
130 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"), server_address, server_port); 127 }
128 die(result, _("TIME UNKNOWN - no data received from server %s, port %d\n"),
129 config.server_address, config.server_port);
131 } 130 }
132 131
133 result = STATE_OK; 132 result = STATE_OK;
134 133
135 time_t conntime = (end_time - start_time); 134 time_t conntime = (end_time - start_time);
136 if (check_critical_time && conntime > critical_time) 135 if (config.check_critical_time && conntime > config.critical_time) {
137 result = STATE_CRITICAL; 136 result = STATE_CRITICAL;
138 else if (check_warning_time && conntime > warning_time) 137 } else if (config.check_warning_time && conntime > config.warning_time) {
139 result = STATE_WARNING; 138 result = STATE_WARNING;
139 }
140 140
141 if (result != STATE_OK) 141 if (result != STATE_OK) {
142 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime, 142 die(result, _("TIME %s - %d second response time|%s\n"), state_text(result), (int)conntime,
143 perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, 143 perfdata("time", (long)conntime, "s", config.check_warning_time,
144 false, 0)); 144 (long)config.warning_time, config.check_critical_time,
145 (long)config.critical_time, true, 0, false, 0));
146 }
145 147
148 unsigned long server_time;
149 unsigned long diff_time;
146 server_time = ntohl(raw_server_time) - UNIX_EPOCH; 150 server_time = ntohl(raw_server_time) - UNIX_EPOCH;
147 if (server_time > (unsigned long)end_time) 151 if (server_time > (unsigned long)end_time) {
148 diff_time = server_time - (unsigned long)end_time; 152 diff_time = server_time - (unsigned long)end_time;
149 else 153 } else {
150 diff_time = (unsigned long)end_time - server_time; 154 diff_time = (unsigned long)end_time - server_time;
155 }
151 156
152 if (check_critical_diff && diff_time > critical_diff) 157 if (config.check_critical_diff && diff_time > config.critical_diff) {
153 result = STATE_CRITICAL; 158 result = STATE_CRITICAL;
154 else if (check_warning_diff && diff_time > warning_diff) 159 } else if (config.check_warning_diff && diff_time > config.warning_diff) {
155 result = STATE_WARNING; 160 result = STATE_WARNING;
161 }
156 162
157 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time, 163 printf(_("TIME %s - %lu second time difference|%s %s\n"), state_text(result), diff_time,
158 perfdata("time", (long)conntime, "s", check_warning_time, (long)warning_time, check_critical_time, (long)critical_time, true, 0, 164 perfdata("time", (long)conntime, "s", config.check_warning_time,
159 false, 0), 165 (long)config.warning_time, config.check_critical_time,
160 perfdata("offset", diff_time, "s", check_warning_diff, warning_diff, check_critical_diff, critical_diff, true, 0, false, 0)); 166 (long)config.critical_time, true, 0, false, 0),
167 perfdata("offset", diff_time, "s", config.check_warning_diff, config.warning_diff,
168 config.check_critical_diff, config.critical_diff, true, 0, false, 0));
161 return result; 169 return result;
162} 170}
163 171
164/* process command-line arguments */ 172/* process command-line arguments */
165int process_arguments(int argc, char **argv) { 173check_time_config_wrapper process_arguments(int argc, char **argv) {
166 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 174 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
167 {"warning-variance", required_argument, 0, 'w'}, 175 {"warning-variance", required_argument, 0, 'w'},
168 {"critical-variance", required_argument, 0, 'c'}, 176 {"critical-variance", required_argument, 0, 'c'},
@@ -175,29 +183,37 @@ int process_arguments(int argc, char **argv) {
175 {"help", no_argument, 0, 'h'}, 183 {"help", no_argument, 0, 'h'},
176 {0, 0, 0, 0}}; 184 {0, 0, 0, 0}};
177 185
178 if (argc < 2) 186 if (argc < 2) {
179 usage("\n"); 187 usage("\n");
188 }
180 189
181 for (int i = 1; i < argc; i++) { 190 for (int i = 1; i < argc; i++) {
182 if (strcmp("-to", argv[i]) == 0) 191 if (strcmp("-to", argv[i]) == 0) {
183 strcpy(argv[i], "-t"); 192 strcpy(argv[i], "-t");
184 else if (strcmp("-wd", argv[i]) == 0) 193 } else if (strcmp("-wd", argv[i]) == 0) {
185 strcpy(argv[i], "-w"); 194 strcpy(argv[i], "-w");
186 else if (strcmp("-cd", argv[i]) == 0) 195 } else if (strcmp("-cd", argv[i]) == 0) {
187 strcpy(argv[i], "-c"); 196 strcpy(argv[i], "-c");
188 else if (strcmp("-wt", argv[i]) == 0) 197 } else if (strcmp("-wt", argv[i]) == 0) {
189 strcpy(argv[i], "-W"); 198 strcpy(argv[i], "-W");
190 else if (strcmp("-ct", argv[i]) == 0) 199 } else if (strcmp("-ct", argv[i]) == 0) {
191 strcpy(argv[i], "-C"); 200 strcpy(argv[i], "-C");
201 }
192 } 202 }
193 203
204 check_time_config_wrapper result = {
205 .errorcode = OK,
206 .config = check_time_config_init(),
207 };
208
194 int option_char; 209 int option_char;
195 while (true) { 210 while (true) {
196 int option = 0; 211 int option = 0;
197 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option); 212 option_char = getopt_long(argc, argv, "hVH:w:c:W:C:p:t:u", longopts, &option);
198 213
199 if (option_char == -1 || option_char == EOF) 214 if (option_char == -1 || option_char == EOF) {
200 break; 215 break;
216 }
201 217
202 switch (option_char) { 218 switch (option_char) {
203 case '?': /* print short usage statement if args not parsable */ 219 case '?': /* print short usage statement if args not parsable */
@@ -209,18 +225,20 @@ int process_arguments(int argc, char **argv) {
209 print_revision(progname, NP_VERSION); 225 print_revision(progname, NP_VERSION);
210 exit(STATE_UNKNOWN); 226 exit(STATE_UNKNOWN);
211 case 'H': /* hostname */ 227 case 'H': /* hostname */
212 if (!is_host(optarg)) 228 if (!is_host(optarg)) {
213 usage2(_("Invalid hostname/address"), optarg); 229 usage2(_("Invalid hostname/address"), optarg);
214 server_address = optarg; 230 }
231 result.config.server_address = optarg;
215 break; 232 break;
216 case 'w': /* warning-variance */ 233 case 'w': /* warning-variance */
217 if (is_intnonneg(optarg)) { 234 if (is_intnonneg(optarg)) {
218 warning_diff = strtoul(optarg, NULL, 10); 235 result.config.warning_diff = strtoul(optarg, NULL, 10);
219 check_warning_diff = true; 236 result.config.check_warning_diff = true;
220 } else if (strspn(optarg, "0123456789:,") > 0) { 237 } else if (strspn(optarg, "0123456789:,") > 0) {
221 if (sscanf(optarg, "%lu%*[:,]%d", &warning_diff, &warning_time) == 2) { 238 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.warning_diff,
222 check_warning_diff = true; 239 &result.config.warning_time) == 2) {
223 check_warning_time = true; 240 result.config.check_warning_diff = true;
241 result.config.check_warning_time = true;
224 } else { 242 } else {
225 usage4(_("Warning thresholds must be a positive integer")); 243 usage4(_("Warning thresholds must be a positive integer"));
226 } 244 }
@@ -230,12 +248,13 @@ int process_arguments(int argc, char **argv) {
230 break; 248 break;
231 case 'c': /* critical-variance */ 249 case 'c': /* critical-variance */
232 if (is_intnonneg(optarg)) { 250 if (is_intnonneg(optarg)) {
233 critical_diff = strtoul(optarg, NULL, 10); 251 result.config.critical_diff = strtoul(optarg, NULL, 10);
234 check_critical_diff = true; 252 result.config.check_critical_diff = true;
235 } else if (strspn(optarg, "0123456789:,") > 0) { 253 } else if (strspn(optarg, "0123456789:,") > 0) {
236 if (sscanf(optarg, "%lu%*[:,]%d", &critical_diff, &critical_time) == 2) { 254 if (sscanf(optarg, "%lu%*[:,]%d", &result.config.critical_diff,
237 check_critical_diff = true; 255 &result.config.critical_time) == 2) {
238 check_critical_time = true; 256 result.config.check_critical_diff = true;
257 result.config.check_critical_time = true;
239 } else { 258 } else {
240 usage4(_("Critical thresholds must be a positive integer")); 259 usage4(_("Critical thresholds must be a positive integer"));
241 } 260 }
@@ -244,48 +263,53 @@ int process_arguments(int argc, char **argv) {
244 } 263 }
245 break; 264 break;
246 case 'W': /* warning-connect */ 265 case 'W': /* warning-connect */
247 if (!is_intnonneg(optarg)) 266 if (!is_intnonneg(optarg)) {
248 usage4(_("Warning threshold must be a positive integer")); 267 usage4(_("Warning threshold must be a positive integer"));
249 else 268 } else {
250 warning_time = atoi(optarg); 269 result.config.warning_time = atoi(optarg);
251 check_warning_time = true; 270 }
271 result.config.check_warning_time = true;
252 break; 272 break;
253 case 'C': /* critical-connect */ 273 case 'C': /* critical-connect */
254 if (!is_intnonneg(optarg)) 274 if (!is_intnonneg(optarg)) {
255 usage4(_("Critical threshold must be a positive integer")); 275 usage4(_("Critical threshold must be a positive integer"));
256 else 276 } else {
257 critical_time = atoi(optarg); 277 result.config.critical_time = atoi(optarg);
258 check_critical_time = true; 278 }
279 result.config.check_critical_time = true;
259 break; 280 break;
260 case 'p': /* port */ 281 case 'p': /* port */
261 if (!is_intnonneg(optarg)) 282 if (!is_intnonneg(optarg)) {
262 usage4(_("Port must be a positive integer")); 283 usage4(_("Port must be a positive integer"));
263 else 284 } else {
264 server_port = atoi(optarg); 285 result.config.server_port = atoi(optarg);
286 }
265 break; 287 break;
266 case 't': /* timeout */ 288 case 't': /* timeout */
267 if (!is_intnonneg(optarg)) 289 if (!is_intnonneg(optarg)) {
268 usage2(_("Timeout interval must be a positive integer"), optarg); 290 usage2(_("Timeout interval must be a positive integer"), optarg);
269 else 291 } else {
270 socket_timeout = atoi(optarg); 292 socket_timeout = atoi(optarg);
293 }
271 break; 294 break;
272 case 'u': /* udp */ 295 case 'u': /* udp */
273 use_udp = true; 296 result.config.use_udp = true;
274 } 297 }
275 } 298 }
276 299
277 option_char = optind; 300 option_char = optind;
278 if (server_address == NULL) { 301 if (result.config.server_address == NULL) {
279 if (argc > option_char) { 302 if (argc > option_char) {
280 if (!is_host(argv[option_char])) 303 if (!is_host(argv[option_char])) {
281 usage2(_("Invalid hostname/address"), optarg); 304 usage2(_("Invalid hostname/address"), optarg);
282 server_address = argv[option_char]; 305 }
306 result.config.server_address = argv[option_char];
283 } else { 307 } else {
284 usage4(_("Hostname was not supplied")); 308 usage4(_("Hostname was not supplied"));
285 } 309 }
286 } 310 }
287 311
288 return OK; 312 return result;
289} 313}
290 314
291void print_help(void) { 315void print_help(void) {
diff --git a/plugins/check_time.d/config.h b/plugins/check_time.d/config.h
new file mode 100644
index 00000000..09bd7c45
--- /dev/null
+++ b/plugins/check_time.d/config.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6enum {
7 TIME_PORT = 37
8};
9
10typedef struct {
11 char *server_address;
12 int server_port;
13 bool use_udp;
14
15 int warning_time;
16 bool check_warning_time;
17 int critical_time;
18 bool check_critical_time;
19 unsigned long warning_diff;
20 bool check_warning_diff;
21 unsigned long critical_diff;
22 bool check_critical_diff;
23} check_time_config;
24
25check_time_config check_time_config_init() {
26 check_time_config tmp = {
27 .server_address = NULL,
28 .server_port = TIME_PORT,
29 .use_udp = false,
30
31 .warning_time = 0,
32 .check_warning_time = false,
33 .critical_time = 0,
34 .check_critical_time = false,
35
36 .warning_diff = 0,
37 .check_warning_diff = false,
38 .critical_diff = 0,
39 .check_critical_diff = false,
40 };
41 return tmp;
42}
diff --git a/plugins/check_ups.c b/plugins/check_ups.c
index 526a29df..54decce3 100644
--- a/plugins/check_ups.c
+++ b/plugins/check_ups.c
@@ -39,69 +39,29 @@ const char *email = "devel@monitoring-plugins.org";
39#include "common.h" 39#include "common.h"
40#include "netutils.h" 40#include "netutils.h"
41#include "utils.h" 41#include "utils.h"
42 42#include "check_ups.d/config.h"
43enum { 43#include "states.h"
44 PORT = 3493
45};
46
47#define UPS_NONE 0 /* no supported options */
48#define UPS_UTILITY 1 /* supports utility line */
49#define UPS_BATTPCT 2 /* supports percent battery remaining */
50#define UPS_STATUS 4 /* supports UPS status */
51#define UPS_TEMP 8 /* supports UPS temperature */
52#define UPS_LOADPCT 16 /* supports load percent */
53#define UPS_REALPOWER 32 /* supports real power */
54
55#define UPSSTATUS_NONE 0
56#define UPSSTATUS_OFF 1
57#define UPSSTATUS_OL 2
58#define UPSSTATUS_OB 4
59#define UPSSTATUS_LB 8
60#define UPSSTATUS_CAL 16
61#define UPSSTATUS_RB 32 /*Replace Battery */
62#define UPSSTATUS_BYPASS 64
63#define UPSSTATUS_OVER 128
64#define UPSSTATUS_TRIM 256
65#define UPSSTATUS_BOOST 512
66#define UPSSTATUS_CHRG 1024
67#define UPSSTATUS_DISCHRG 2048
68#define UPSSTATUS_UNKNOWN 4096
69#define UPSSTATUS_ALARM 8192
70 44
71enum { 45enum {
72 NOSUCHVAR = ERROR - 1 46 NOSUCHVAR = ERROR - 1
73}; 47};
74 48
75typedef struct ups_config {
76 unsigned int server_port;
77 char *server_address;
78 char *ups_name;
79 double warning_value;
80 double critical_value;
81 bool check_warn;
82 bool check_crit;
83 int check_variable;
84 int status;
85 bool temp_output_c;
86} ups_config;
87
88ups_config ups_config_init(void) {
89 ups_config tmp = {0};
90 tmp.server_port = PORT;
91 tmp.server_address = NULL;
92 tmp.ups_name = NULL;
93 tmp.check_variable = UPS_NONE;
94 tmp.status = UPSSTATUS_NONE;
95
96 return tmp;
97}
98
99// Forward declarations 49// Forward declarations
100static int determine_status(ups_config * /*config*/, int *supported_options); 50typedef struct {
101static int get_ups_variable(const char * /*varname*/, char * /*buf*/, ups_config config); 51 int errorcode;
52 int ups_status;
53 int supported_options;
54} determine_status_result;
55static determine_status_result determine_status(check_ups_config /*config*/);
56static int get_ups_variable(const char * /*varname*/, char * /*buf*/, check_ups_config config);
57
58typedef struct {
59 int errorcode;
60 check_ups_config config;
61} check_ups_config_wrapper;
62static check_ups_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
63static check_ups_config_wrapper validate_arguments(check_ups_config_wrapper /*config_wrapper*/);
102 64
103static int process_arguments(int /*argc*/, char ** /*argv*/, ups_config * /*config*/);
104static int validate_arguments(ups_config /*config*/);
105static void print_help(void); 65static void print_help(void);
106void print_usage(void); 66void print_usage(void);
107 67
@@ -109,28 +69,16 @@ int main(int argc, char **argv) {
109 setlocale(LC_ALL, ""); 69 setlocale(LC_ALL, "");
110 bindtextdomain(PACKAGE, LOCALEDIR); 70 bindtextdomain(PACKAGE, LOCALEDIR);
111 textdomain(PACKAGE); 71 textdomain(PACKAGE);
112
113 char *ups_status;
114 ups_status = strdup("N/A");
115
116 char *data;
117 data = strdup("");
118
119 char *message;
120 message = strdup("");
121
122 // Exit result
123 int result = STATE_UNKNOWN;
124
125 /* Parse extra opts if any */ 72 /* Parse extra opts if any */
126 argv = np_extra_opts(&argc, argv, progname); 73 argv = np_extra_opts(&argc, argv, progname);
127 74
128 // Config from commandline 75 check_ups_config_wrapper tmp_config = process_arguments(argc, argv);
129 ups_config config = ups_config_init();
130 76
131 if (process_arguments(argc, argv, &config) == ERROR) { 77 if (tmp_config.errorcode == ERROR) {
132 usage4(_("Could not parse arguments")); 78 usage4(_("Could not parse arguments"));
133 } 79 }
80 // Config from commandline
81 check_ups_config config = tmp_config.config;
134 82
135 /* initialize alarm signal handling */ 83 /* initialize alarm signal handling */
136 signal(SIGALRM, socket_timeout_alarm_handler); 84 signal(SIGALRM, socket_timeout_alarm_handler);
@@ -138,71 +86,76 @@ int main(int argc, char **argv) {
138 /* set socket timeout */ 86 /* set socket timeout */
139 alarm(socket_timeout); 87 alarm(socket_timeout);
140 88
141 int supported_options = UPS_NONE;
142
143 /* get the ups status if possible */ 89 /* get the ups status if possible */
144 if (determine_status(&config, &supported_options) != OK) { 90 determine_status_result query_result = determine_status(config);
91 if (query_result.errorcode != OK) {
145 return STATE_CRITICAL; 92 return STATE_CRITICAL;
146 } 93 }
147 94
148 if (supported_options & UPS_STATUS) { 95 int ups_status_flags = query_result.ups_status;
96 int supported_options = query_result.supported_options;
149 97
150 ups_status = strdup(""); 98 // Exit result
99 mp_state_enum result = STATE_UNKNOWN;
100 char *message = NULL;
151 101
102 if (supported_options & UPS_STATUS) {
103 char *ups_status = strdup("");
152 result = STATE_OK; 104 result = STATE_OK;
153 105
154 if (config.status & UPSSTATUS_OFF) { 106 if (ups_status_flags & UPSSTATUS_OFF) {
155 xasprintf(&ups_status, "Off"); 107 xasprintf(&ups_status, "Off");
156 result = STATE_CRITICAL; 108 result = STATE_CRITICAL;
157 } else if ((config.status & (UPSSTATUS_OB | UPSSTATUS_LB)) == (UPSSTATUS_OB | UPSSTATUS_LB)) { 109 } else if ((ups_status_flags & (UPSSTATUS_OB | UPSSTATUS_LB)) ==
110 (UPSSTATUS_OB | UPSSTATUS_LB)) {
158 xasprintf(&ups_status, _("On Battery, Low Battery")); 111 xasprintf(&ups_status, _("On Battery, Low Battery"));
159 result = STATE_CRITICAL; 112 result = STATE_CRITICAL;
160 } else { 113 } else {
161 if (config.status & UPSSTATUS_OL) { 114 if (ups_status_flags & UPSSTATUS_OL) {
162 xasprintf(&ups_status, "%s%s", ups_status, _("Online")); 115 xasprintf(&ups_status, "%s%s", ups_status, _("Online"));
163 } 116 }
164 if (config.status & UPSSTATUS_OB) { 117 if (ups_status_flags & UPSSTATUS_OB) {
165 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery")); 118 xasprintf(&ups_status, "%s%s", ups_status, _("On Battery"));
166 result = max_state(result, STATE_WARNING); 119 result = max_state(result, STATE_WARNING);
167 } 120 }
168 if (config.status & UPSSTATUS_LB) { 121 if (ups_status_flags & UPSSTATUS_LB) {
169 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery")); 122 xasprintf(&ups_status, "%s%s", ups_status, _(", Low Battery"));
170 result = max_state(result, STATE_WARNING); 123 result = max_state(result, STATE_WARNING);
171 } 124 }
172 if (config.status & UPSSTATUS_CAL) { 125 if (ups_status_flags & UPSSTATUS_CAL) {
173 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating")); 126 xasprintf(&ups_status, "%s%s", ups_status, _(", Calibrating"));
174 } 127 }
175 if (config.status & UPSSTATUS_RB) { 128 if (ups_status_flags & UPSSTATUS_RB) {
176 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery")); 129 xasprintf(&ups_status, "%s%s", ups_status, _(", Replace Battery"));
177 result = max_state(result, STATE_WARNING); 130 result = max_state(result, STATE_WARNING);
178 } 131 }
179 if (config.status & UPSSTATUS_BYPASS) { 132 if (ups_status_flags & UPSSTATUS_BYPASS) {
180 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass")); 133 xasprintf(&ups_status, "%s%s", ups_status, _(", On Bypass"));
181 // Bypassing the battery is likely a bad thing 134 // Bypassing the battery is likely a bad thing
182 result = STATE_CRITICAL; 135 result = STATE_CRITICAL;
183 } 136 }
184 if (config.status & UPSSTATUS_OVER) { 137 if (ups_status_flags & UPSSTATUS_OVER) {
185 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload")); 138 xasprintf(&ups_status, "%s%s", ups_status, _(", Overload"));
186 result = max_state(result, STATE_WARNING); 139 result = max_state(result, STATE_WARNING);
187 } 140 }
188 if (config.status & UPSSTATUS_TRIM) { 141 if (ups_status_flags & UPSSTATUS_TRIM) {
189 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming")); 142 xasprintf(&ups_status, "%s%s", ups_status, _(", Trimming"));
190 } 143 }
191 if (config.status & UPSSTATUS_BOOST) { 144 if (ups_status_flags & UPSSTATUS_BOOST) {
192 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting")); 145 xasprintf(&ups_status, "%s%s", ups_status, _(", Boosting"));
193 } 146 }
194 if (config.status & UPSSTATUS_CHRG) { 147 if (ups_status_flags & UPSSTATUS_CHRG) {
195 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging")); 148 xasprintf(&ups_status, "%s%s", ups_status, _(", Charging"));
196 } 149 }
197 if (config.status & UPSSTATUS_DISCHRG) { 150 if (ups_status_flags & UPSSTATUS_DISCHRG) {
198 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging")); 151 xasprintf(&ups_status, "%s%s", ups_status, _(", Discharging"));
199 result = max_state(result, STATE_WARNING); 152 result = max_state(result, STATE_WARNING);
200 } 153 }
201 if (config.status & UPSSTATUS_ALARM) { 154 if (ups_status_flags & UPSSTATUS_ALARM) {
202 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM")); 155 xasprintf(&ups_status, "%s%s", ups_status, _(", ALARM"));
203 result = STATE_CRITICAL; 156 result = STATE_CRITICAL;
204 } 157 }
205 if (config.status & UPSSTATUS_UNKNOWN) { 158 if (ups_status_flags & UPSSTATUS_UNKNOWN) {
206 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown")); 159 xasprintf(&ups_status, "%s%s", ups_status, _(", Unknown"));
207 } 160 }
208 } 161 }
@@ -211,7 +164,7 @@ int main(int argc, char **argv) {
211 164
212 int res; 165 int res;
213 char temp_buffer[MAX_INPUT_BUFFER]; 166 char temp_buffer[MAX_INPUT_BUFFER];
214 167 char *performance_data = strdup("");
215 /* get the ups utility voltage if possible */ 168 /* get the ups utility voltage if possible */
216 res = get_ups_variable("input.voltage", temp_buffer, config); 169 res = get_ups_variable("input.voltage", temp_buffer, config);
217 if (res == NOSUCHVAR) { 170 if (res == NOSUCHVAR) {
@@ -239,11 +192,15 @@ int main(int argc, char **argv) {
239 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) { 192 } else if (config.check_warn && ups_utility_deviation >= config.warning_value) {
240 result = max_state(result, STATE_WARNING); 193 result = max_state(result, STATE_WARNING);
241 } 194 }
242 xasprintf(&data, "%s", 195 xasprintf(&performance_data, "%s",
243 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", config.check_warn, (long)(1000 * config.warning_value), 196 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV",
244 config.check_crit, (long)(1000 * config.critical_value), true, 0, false, 0)); 197 config.check_warn, (long)(1000 * config.warning_value),
198 config.check_crit, (long)(1000 * config.critical_value), true, 0,
199 false, 0));
245 } else { 200 } else {
246 xasprintf(&data, "%s", perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false, 0, true, 0, false, 0)); 201 xasprintf(&performance_data, "%s",
202 perfdata("voltage", (long)(1000 * ups_utility_voltage), "mV", false, 0, false,
203 0, true, 0, false, 0));
247 } 204 }
248 } 205 }
249 206
@@ -266,11 +223,14 @@ int main(int argc, char **argv) {
266 } else if (config.check_warn && ups_battery_percent <= config.warning_value) { 223 } else if (config.check_warn && ups_battery_percent <= config.warning_value) {
267 result = max_state(result, STATE_WARNING); 224 result = max_state(result, STATE_WARNING);
268 } 225 }
269 xasprintf(&data, "%s %s", data, 226 xasprintf(&performance_data, "%s %s", performance_data,
270 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn, (long)(config.warning_value), 227 perfdata("battery", (long)ups_battery_percent, "%", config.check_warn,
271 config.check_crit, (long)(config.critical_value), true, 0, true, 100)); 228 (long)(config.warning_value), config.check_crit,
229 (long)(config.critical_value), true, 0, true, 100));
272 } else { 230 } else {
273 xasprintf(&data, "%s %s", data, perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true, 0, true, 100)); 231 xasprintf(&performance_data, "%s %s", performance_data,
232 perfdata("battery", (long)ups_battery_percent, "%", false, 0, false, 0, true,
233 0, true, 100));
274 } 234 }
275 } 235 }
276 236
@@ -293,11 +253,14 @@ int main(int argc, char **argv) {
293 } else if (config.check_warn && ups_load_percent >= config.warning_value) { 253 } else if (config.check_warn && ups_load_percent >= config.warning_value) {
294 result = max_state(result, STATE_WARNING); 254 result = max_state(result, STATE_WARNING);
295 } 255 }
296 xasprintf(&data, "%s %s", data, 256 xasprintf(&performance_data, "%s %s", performance_data,
297 perfdata("load", (long)ups_load_percent, "%", config.check_warn, (long)(config.warning_value), config.check_crit, 257 perfdata("load", (long)ups_load_percent, "%", config.check_warn,
258 (long)(config.warning_value), config.check_crit,
298 (long)(config.critical_value), true, 0, true, 100)); 259 (long)(config.critical_value), true, 0, true, 100));
299 } else { 260 } else {
300 xasprintf(&data, "%s %s", data, perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0, true, 100)); 261 xasprintf(&performance_data, "%s %s", performance_data,
262 perfdata("load", (long)ups_load_percent, "%", false, 0, false, 0, true, 0,
263 true, 100));
301 } 264 }
302 } 265 }
303 266
@@ -329,11 +292,14 @@ int main(int argc, char **argv) {
329 } else if (config.check_warn && ups_temperature >= config.warning_value) { 292 } else if (config.check_warn && ups_temperature >= config.warning_value) {
330 result = max_state(result, STATE_WARNING); 293 result = max_state(result, STATE_WARNING);
331 } 294 }
332 xasprintf(&data, "%s %s", data, 295 xasprintf(&performance_data, "%s %s", performance_data,
333 perfdata("temp", (long)ups_temperature, tunits, config.check_warn, (long)(config.warning_value), config.check_crit, 296 perfdata("temp", (long)ups_temperature, tunits, config.check_warn,
297 (long)(config.warning_value), config.check_crit,
334 (long)(config.critical_value), true, 0, false, 0)); 298 (long)(config.critical_value), true, 0, false, 0));
335 } else { 299 } else {
336 xasprintf(&data, "%s %s", data, perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0, false, 0)); 300 xasprintf(&performance_data, "%s %s", performance_data,
301 perfdata("temp", (long)ups_temperature, tunits, false, 0, false, 0, true, 0,
302 false, 0));
337 } 303 }
338 } 304 }
339 305
@@ -355,11 +321,14 @@ int main(int argc, char **argv) {
355 } else if (config.check_warn && ups_realpower >= config.warning_value) { 321 } else if (config.check_warn && ups_realpower >= config.warning_value) {
356 result = max_state(result, STATE_WARNING); 322 result = max_state(result, STATE_WARNING);
357 } 323 }
358 xasprintf(&data, "%s %s", data, 324 xasprintf(&performance_data, "%s %s", performance_data,
359 perfdata("realpower", (long)ups_realpower, "W", config.check_warn, (long)(config.warning_value), config.check_crit, 325 perfdata("realpower", (long)ups_realpower, "W", config.check_warn,
326 (long)(config.warning_value), config.check_crit,
360 (long)(config.critical_value), true, 0, false, 0)); 327 (long)(config.critical_value), true, 0, false, 0));
361 } else { 328 } else {
362 xasprintf(&data, "%s %s", data, perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0, false, 0)); 329 xasprintf(&performance_data, "%s %s", performance_data,
330 perfdata("realpower", (long)ups_realpower, "W", false, 0, false, 0, true, 0,
331 false, 0));
363 } 332 }
364 } 333 }
365 334
@@ -373,71 +342,79 @@ int main(int argc, char **argv) {
373 /* reset timeout */ 342 /* reset timeout */
374 alarm(0); 343 alarm(0);
375 344
376 printf("UPS %s - %s|%s\n", state_text(result), message, data); 345 printf("UPS %s - %s|%s\n", state_text(result), message, performance_data);
377 return result; 346 exit(result);
378} 347}
379 348
380/* determines what options are supported by the UPS */ 349/* determines what options are supported by the UPS */
381int determine_status(ups_config *config, int *supported_options) { 350determine_status_result determine_status(const check_ups_config config) {
382 char recv_buffer[MAX_INPUT_BUFFER];
383 351
384 int res = get_ups_variable("ups.status", recv_buffer, *config); 352 determine_status_result result = {
353 .errorcode = OK,
354 .ups_status = UPSSTATUS_NONE,
355 .supported_options = 0,
356 };
357
358 char recv_buffer[MAX_INPUT_BUFFER];
359 int res = get_ups_variable("ups.status", recv_buffer, config);
385 if (res == NOSUCHVAR) { 360 if (res == NOSUCHVAR) {
386 return OK; 361 return result;
387 } 362 }
388 363
389 if (res != STATE_OK) { 364 if (res != STATE_OK) {
390 printf("%s\n", _("Invalid response received from host")); 365 printf("%s\n", _("Invalid response received from host"));
391 return ERROR; 366 result.errorcode = ERROR;
367 return result;
392 } 368 }
393 369
394 *supported_options |= UPS_STATUS; 370 result.supported_options |= UPS_STATUS;
395 371
396 char temp_buffer[MAX_INPUT_BUFFER]; 372 char temp_buffer[MAX_INPUT_BUFFER];
397 373
398 strcpy(temp_buffer, recv_buffer); 374 strcpy(temp_buffer, recv_buffer);
399 for (char *ptr = (char *)strtok(temp_buffer, " "); ptr != NULL; ptr = (char *)strtok(NULL, " ")) { 375 for (char *ptr = strtok(temp_buffer, " "); ptr != NULL; ptr = strtok(NULL, " ")) {
400 if (!strcmp(ptr, "OFF")) { 376 if (!strcmp(ptr, "OFF")) {
401 config->status |= UPSSTATUS_OFF; 377 result.ups_status |= UPSSTATUS_OFF;
402 } else if (!strcmp(ptr, "OL")) { 378 } else if (!strcmp(ptr, "OL")) {
403 config->status |= UPSSTATUS_OL; 379 result.ups_status |= UPSSTATUS_OL;
404 } else if (!strcmp(ptr, "OB")) { 380 } else if (!strcmp(ptr, "OB")) {
405 config->status |= UPSSTATUS_OB; 381 result.ups_status |= UPSSTATUS_OB;
406 } else if (!strcmp(ptr, "LB")) { 382 } else if (!strcmp(ptr, "LB")) {
407 config->status |= UPSSTATUS_LB; 383 result.ups_status |= UPSSTATUS_LB;
408 } else if (!strcmp(ptr, "CAL")) { 384 } else if (!strcmp(ptr, "CAL")) {
409 config->status |= UPSSTATUS_CAL; 385 result.ups_status |= UPSSTATUS_CAL;
410 } else if (!strcmp(ptr, "RB")) { 386 } else if (!strcmp(ptr, "RB")) {
411 config->status |= UPSSTATUS_RB; 387 result.ups_status |= UPSSTATUS_RB;
412 } else if (!strcmp(ptr, "BYPASS")) { 388 } else if (!strcmp(ptr, "BYPASS")) {
413 config->status |= UPSSTATUS_BYPASS; 389 result.ups_status |= UPSSTATUS_BYPASS;
414 } else if (!strcmp(ptr, "OVER")) { 390 } else if (!strcmp(ptr, "OVER")) {
415 config->status |= UPSSTATUS_OVER; 391 result.ups_status |= UPSSTATUS_OVER;
416 } else if (!strcmp(ptr, "TRIM")) { 392 } else if (!strcmp(ptr, "TRIM")) {
417 config->status |= UPSSTATUS_TRIM; 393 result.ups_status |= UPSSTATUS_TRIM;
418 } else if (!strcmp(ptr, "BOOST")) { 394 } else if (!strcmp(ptr, "BOOST")) {
419 config->status |= UPSSTATUS_BOOST; 395 result.ups_status |= UPSSTATUS_BOOST;
420 } else if (!strcmp(ptr, "CHRG")) { 396 } else if (!strcmp(ptr, "CHRG")) {
421 config->status |= UPSSTATUS_CHRG; 397 result.ups_status |= UPSSTATUS_CHRG;
422 } else if (!strcmp(ptr, "DISCHRG")) { 398 } else if (!strcmp(ptr, "DISCHRG")) {
423 config->status |= UPSSTATUS_DISCHRG; 399 result.ups_status |= UPSSTATUS_DISCHRG;
424 } else if (!strcmp(ptr, "ALARM")) { 400 } else if (!strcmp(ptr, "ALARM")) {
425 config->status |= UPSSTATUS_ALARM; 401 result.ups_status |= UPSSTATUS_ALARM;
426 } else { 402 } else {
427 config->status |= UPSSTATUS_UNKNOWN; 403 result.ups_status |= UPSSTATUS_UNKNOWN;
428 } 404 }
429 } 405 }
430 406
431 return OK; 407 return result;
432} 408}
433 409
434/* gets a variable value for a specific UPS */ 410/* gets a variable value for a specific UPS */
435int get_ups_variable(const char *varname, char *buf, const ups_config config) { 411int get_ups_variable(const char *varname, char *buf, const check_ups_config config) {
436 char send_buffer[MAX_INPUT_BUFFER]; 412 char send_buffer[MAX_INPUT_BUFFER];
437 413
438 /* create the command string to send to the UPS daemon */ 414 /* create the command string to send to the UPS daemon */
439 /* Add LOGOUT to avoid read failure logs */ 415 /* Add LOGOUT to avoid read failure logs */
440 int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name, varname); 416 int res = snprintf(send_buffer, sizeof(send_buffer), "GET VAR %s %s\nLOGOUT\n", config.ups_name,
417 varname);
441 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) { 418 if ((res > 0) && ((size_t)res >= sizeof(send_buffer))) {
442 printf("%s\n", _("UPS name to long for buffer")); 419 printf("%s\n", _("UPS name to long for buffer"));
443 return ERROR; 420 return ERROR;
@@ -446,7 +423,8 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
446 char temp_buffer[MAX_INPUT_BUFFER]; 423 char temp_buffer[MAX_INPUT_BUFFER];
447 424
448 /* send the command to the daemon and get a response back */ 425 /* send the command to the daemon and get a response back */
449 if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer, sizeof(temp_buffer)) != STATE_OK) { 426 if (process_tcp_request(config.server_address, config.server_port, send_buffer, temp_buffer,
427 sizeof(temp_buffer)) != STATE_OK) {
450 printf("%s\n", _("Invalid response received from host")); 428 printf("%s\n", _("Invalid response received from host"));
451 return ERROR; 429 return ERROR;
452 } 430 }
@@ -500,7 +478,7 @@ int get_ups_variable(const char *varname, char *buf, const ups_config config) {
500 [-wv warn_value] [-cv crit_value] [-to to_sec] */ 478 [-wv warn_value] [-cv crit_value] [-to to_sec] */
501 479
502/* process command-line arguments */ 480/* process command-line arguments */
503int process_arguments(int argc, char **argv, ups_config *config) { 481check_ups_config_wrapper process_arguments(int argc, char **argv) {
504 482
505 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 483 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
506 {"ups", required_argument, 0, 'u'}, 484 {"ups", required_argument, 0, 'u'},
@@ -514,8 +492,14 @@ int process_arguments(int argc, char **argv, ups_config *config) {
514 {"help", no_argument, 0, 'h'}, 492 {"help", no_argument, 0, 'h'},
515 {0, 0, 0, 0}}; 493 {0, 0, 0, 0}};
516 494
495 check_ups_config_wrapper result = {
496 .errorcode = OK,
497 .config = check_ups_config_init(),
498 };
499
517 if (argc < 2) { 500 if (argc < 2) {
518 return ERROR; 501 result.errorcode = ERROR;
502 return result;
519 } 503 }
520 504
521 int c; 505 int c;
@@ -542,52 +526,52 @@ int process_arguments(int argc, char **argv, ups_config *config) {
542 usage5(); 526 usage5();
543 case 'H': /* hostname */ 527 case 'H': /* hostname */
544 if (is_host(optarg)) { 528 if (is_host(optarg)) {
545 config->server_address = optarg; 529 result.config.server_address = optarg;
546 } else { 530 } else {
547 usage2(_("Invalid hostname/address"), optarg); 531 usage2(_("Invalid hostname/address"), optarg);
548 } 532 }
549 break; 533 break;
550 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for 534 case 'T': /* FIXME: to be improved (ie "-T C" for Celsius or "-T F" for
551 Fahrenheit) */ 535 Fahrenheit) */
552 config->temp_output_c = true; 536 result.config.temp_output_c = true;
553 break; 537 break;
554 case 'u': /* ups name */ 538 case 'u': /* ups name */
555 config->ups_name = optarg; 539 result.config.ups_name = optarg;
556 break; 540 break;
557 case 'p': /* port */ 541 case 'p': /* port */
558 if (is_intpos(optarg)) { 542 if (is_intpos(optarg)) {
559 config->server_port = atoi(optarg); 543 result.config.server_port = atoi(optarg);
560 } else { 544 } else {
561 usage2(_("Port must be a positive integer"), optarg); 545 usage2(_("Port must be a positive integer"), optarg);
562 } 546 }
563 break; 547 break;
564 case 'c': /* critical time threshold */ 548 case 'c': /* critical time threshold */
565 if (is_intnonneg(optarg)) { 549 if (is_intnonneg(optarg)) {
566 config->critical_value = atoi(optarg); 550 result.config.critical_value = atoi(optarg);
567 config->check_crit = true; 551 result.config.check_crit = true;
568 } else { 552 } else {
569 usage2(_("Critical time must be a positive integer"), optarg); 553 usage2(_("Critical time must be a positive integer"), optarg);
570 } 554 }
571 break; 555 break;
572 case 'w': /* warning time threshold */ 556 case 'w': /* warning time threshold */
573 if (is_intnonneg(optarg)) { 557 if (is_intnonneg(optarg)) {
574 config->warning_value = atoi(optarg); 558 result.config.warning_value = atoi(optarg);
575 config->check_warn = true; 559 result.config.check_warn = true;
576 } else { 560 } else {
577 usage2(_("Warning time must be a positive integer"), optarg); 561 usage2(_("Warning time must be a positive integer"), optarg);
578 } 562 }
579 break; 563 break;
580 case 'v': /* variable */ 564 case 'v': /* variable */
581 if (!strcmp(optarg, "LINE")) { 565 if (!strcmp(optarg, "LINE")) {
582 config->check_variable = UPS_UTILITY; 566 result.config.check_variable = UPS_UTILITY;
583 } else if (!strcmp(optarg, "TEMP")) { 567 } else if (!strcmp(optarg, "TEMP")) {
584 config->check_variable = UPS_TEMP; 568 result.config.check_variable = UPS_TEMP;
585 } else if (!strcmp(optarg, "BATTPCT")) { 569 } else if (!strcmp(optarg, "BATTPCT")) {
586 config->check_variable = UPS_BATTPCT; 570 result.config.check_variable = UPS_BATTPCT;
587 } else if (!strcmp(optarg, "LOADPCT")) { 571 } else if (!strcmp(optarg, "LOADPCT")) {
588 config->check_variable = UPS_LOADPCT; 572 result.config.check_variable = UPS_LOADPCT;
589 } else if (!strcmp(optarg, "REALPOWER")) { 573 } else if (!strcmp(optarg, "REALPOWER")) {
590 config->check_variable = UPS_REALPOWER; 574 result.config.check_variable = UPS_REALPOWER;
591 } else { 575 } else {
592 usage2(_("Unrecognized UPS variable"), optarg); 576 usage2(_("Unrecognized UPS variable"), optarg);
593 } 577 }
@@ -608,27 +592,27 @@ int process_arguments(int argc, char **argv, ups_config *config) {
608 } 592 }
609 } 593 }
610 594
611 if (config->server_address == NULL && argc > optind) { 595 if (result.config.server_address == NULL && argc > optind) {
612 if (is_host(argv[optind])) { 596 if (is_host(argv[optind])) {
613 config->server_address = argv[optind++]; 597 result.config.server_address = argv[optind++];
614 } else { 598 } else {
615 usage2(_("Invalid hostname/address"), optarg); 599 usage2(_("Invalid hostname/address"), optarg);
616 } 600 }
617 } 601 }
618 602
619 if (config->server_address == NULL) { 603 if (result.config.server_address == NULL) {
620 config->server_address = strdup("127.0.0.1"); 604 result.config.server_address = strdup("127.0.0.1");
621 } 605 }
622 606
623 return validate_arguments(*config); 607 return validate_arguments(result);
624} 608}
625 609
626int validate_arguments(ups_config config) { 610check_ups_config_wrapper validate_arguments(check_ups_config_wrapper config_wrapper) {
627 if (!config.ups_name) { 611 if (config_wrapper.config.ups_name) {
628 printf("%s\n", _("Error : no UPS indicated")); 612 printf("%s\n", _("Error : no UPS indicated"));
629 return ERROR; 613 config_wrapper.errorcode = ERROR;
630 } 614 }
631 return OK; 615 return config_wrapper;
632} 616}
633 617
634void print_help(void) { 618void print_help(void) {
@@ -660,7 +644,8 @@ void print_help(void) {
660 printf(" %s\n", "-T, --temperature"); 644 printf(" %s\n", "-T, --temperature");
661 printf(" %s\n", _("Output of temperatures in Celsius")); 645 printf(" %s\n", _("Output of temperatures in Celsius"));
662 printf(" %s\n", "-v, --variable=STRING"); 646 printf(" %s\n", "-v, --variable=STRING");
663 printf(" %s %s\n", _("Valid values for STRING are"), "LINE, TEMP, BATTPCT, LOADPCT or REALPOWER"); 647 printf(" %s %s\n", _("Valid values for STRING are"),
648 "LINE, TEMP, BATTPCT, LOADPCT or REALPOWER");
664 649
665 printf(UT_WARN_CRIT); 650 printf(UT_WARN_CRIT);
666 651
diff --git a/plugins/check_ups.d/config.h b/plugins/check_ups.d/config.h
new file mode 100644
index 00000000..e05edceb
--- /dev/null
+++ b/plugins/check_ups.d/config.h
@@ -0,0 +1,54 @@
1#pragma once
2
3#include "../../config.h"
4#include <stddef.h>
5
6#define UPS_NONE 0 /* no supported options */
7#define UPS_UTILITY 1 /* supports utility line */
8#define UPS_BATTPCT 2 /* supports percent battery remaining */
9#define UPS_STATUS 4 /* supports UPS status */
10#define UPS_TEMP 8 /* supports UPS temperature */
11#define UPS_LOADPCT 16 /* supports load percent */
12#define UPS_REALPOWER 32 /* supports real power */
13
14#define UPSSTATUS_NONE 0
15#define UPSSTATUS_OFF 1
16#define UPSSTATUS_OL 2
17#define UPSSTATUS_OB 4
18#define UPSSTATUS_LB 8
19#define UPSSTATUS_CAL 16
20#define UPSSTATUS_RB 32 /*Replace Battery */
21#define UPSSTATUS_BYPASS 64
22#define UPSSTATUS_OVER 128
23#define UPSSTATUS_TRIM 256
24#define UPSSTATUS_BOOST 512
25#define UPSSTATUS_CHRG 1024
26#define UPSSTATUS_DISCHRG 2048
27#define UPSSTATUS_UNKNOWN 4096
28#define UPSSTATUS_ALARM 8192
29
30enum {
31 PORT = 3493
32};
33
34typedef struct ups_config {
35 unsigned int server_port;
36 char *server_address;
37 char *ups_name;
38 double warning_value;
39 double critical_value;
40 bool check_warn;
41 bool check_crit;
42 int check_variable;
43 bool temp_output_c;
44} check_ups_config;
45
46check_ups_config check_ups_config_init(void) {
47 check_ups_config tmp = {0};
48 tmp.server_port = PORT;
49 tmp.server_address = NULL;
50 tmp.ups_name = NULL;
51 tmp.check_variable = UPS_NONE;
52
53 return tmp;
54}
diff --git a/plugins/check_users.c b/plugins/check_users.c
index f1e1c39d..2340eae4 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -34,8 +34,15 @@ const char *progname = "check_users";
34const char *copyright = "2000-2024"; 34const char *copyright = "2000-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "common.h" 37#include "check_users.d/users.h"
38#include "utils.h" 38#include "output.h"
39#include "perfdata.h"
40#include "states.h"
41#include "utils_base.h"
42#include "./common.h"
43#include "./utils.h"
44#include "check_users.d/config.h"
45#include "thresholds.h"
39 46
40#if HAVE_WTSAPI32_H 47#if HAVE_WTSAPI32_H
41# include <windows.h> 48# include <windows.h>
@@ -53,29 +60,16 @@ const char *email = "devel@monitoring-plugins.org";
53# include <systemd/sd-login.h> 60# include <systemd/sd-login.h>
54#endif 61#endif
55 62
56#define possibly_set(a, b) ((a) == 0 ? (b) : 0) 63typedef struct process_argument_wrapper {
64 int errorcode;
65 check_users_config config;
66} check_users_config_wrapper;
67check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57 68
58static int process_arguments(int, char **); 69void print_help(void);
59static void print_help(void);
60void print_usage(void); 70void print_usage(void);
61 71
62static char *warning_range = NULL;
63static char *critical_range = NULL;
64static thresholds *thlds = NULL;
65
66int main(int argc, char **argv) { 72int main(int argc, char **argv) {
67 int users = -1;
68 int result = STATE_UNKNOWN;
69#if HAVE_WTSAPI32_H
70 WTS_SESSION_INFO *wtsinfo;
71 DWORD wtscount;
72 DWORD index;
73#elif HAVE_UTMPX_H
74 struct utmpx *putmpx;
75#else
76 char input_buffer[MAX_INPUT_BUFFER];
77#endif
78
79 setlocale(LC_ALL, ""); 73 setlocale(LC_ALL, "");
80 bindtextdomain(PACKAGE, LOCALEDIR); 74 bindtextdomain(PACKAGE, LOCALEDIR);
81 textdomain(PACKAGE); 75 textdomain(PACKAGE);
@@ -83,121 +77,104 @@ int main(int argc, char **argv) {
83 /* Parse extra opts if any */ 77 /* Parse extra opts if any */
84 argv = np_extra_opts(&argc, argv, progname); 78 argv = np_extra_opts(&argc, argv, progname);
85 79
86 if (process_arguments(argc, argv) == ERROR) 80 check_users_config_wrapper tmp_config = process_arguments(argc, argv);
87 usage4(_("Could not parse arguments"));
88
89 users = 0;
90
91#ifdef HAVE_LIBSYSTEMD
92 if (sd_booted() > 0)
93 users = sd_get_sessions(NULL);
94 else {
95#endif
96#if HAVE_WTSAPI32_H
97 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) {
98 printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
99 return STATE_UNKNOWN;
100 }
101
102 for (index = 0; index < wtscount; index++) {
103 LPTSTR username;
104 DWORD size;
105 int len;
106
107 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size))
108 continue;
109
110 len = lstrlen(username);
111
112 WTSFreeMemory(username);
113
114 if (len == 0)
115 continue;
116 81
117 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) 82 if (tmp_config.errorcode == ERROR) {
118 users++; 83 usage4(_("Could not parse arguments"));
119 }
120
121 WTSFreeMemory(wtsinfo);
122#elif HAVE_UTMPX_H
123 /* get currently logged users from utmpx */
124 setutxent();
125
126 while ((putmpx = getutxent()) != NULL)
127 if (putmpx->ut_type == USER_PROCESS)
128 users++;
129
130 endutxent();
131#else
132 /* run the command */
133 child_process = spopen(WHO_COMMAND);
134 if (child_process == NULL) {
135 printf(_("Could not open pipe: %s\n"), WHO_COMMAND);
136 return STATE_UNKNOWN;
137 } 84 }
138 85
139 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); 86 check_users_config config = tmp_config.config;
140 if (child_stderr == NULL)
141 printf(_("Could not open stderr for %s\n"), WHO_COMMAND);
142
143 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
144 /* increment 'users' on all lines except total user count */
145 if (input_buffer[0] != '#') {
146 users++;
147 continue;
148 }
149 87
150 /* get total logged in users */ 88#ifdef _WIN32
151 if (sscanf(input_buffer, _("# users=%d"), &users) == 1) 89# if HAVE_WTSAPI32_H
152 break; 90 get_num_of_users_wrapper user_wrapper = get_num_of_users_windows();
91# else
92# error Did not find WTSAPI32
93# endif // HAVE_WTSAPI32_H
94#else
95# ifdef HAVE_LIBSYSTEMD
96 get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd();
97# elif HAVE_UTMPX_H
98 get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp();
99# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H
100 get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command();
101# endif // HAVE_LIBSYSTEMD
102#endif // _WIN32
103
104 mp_check overall = mp_check_init();
105 if (config.output_format_is_set) {
106 mp_set_format(config.output_format);
153 } 107 }
108 mp_subcheck sc_users = mp_subcheck_init();
154 109
155 /* check STDERR */ 110 if (user_wrapper.errorcode != 0) {
156 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) 111 sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN);
157 result = possibly_set(result, STATE_UNKNOWN); 112 sc_users.output = "Failed to retrieve number of users";
158 (void)fclose(child_stderr); 113 mp_add_subcheck_to_check(&overall, sc_users);
159 114 mp_exit(overall);
160 /* close the pipe */
161 if (spclose(child_process))
162 result = possibly_set(result, STATE_UNKNOWN);
163#endif
164#ifdef HAVE_LIBSYSTEMD
165 } 115 }
166#endif
167
168 /* check the user count against warning and critical thresholds */ 116 /* check the user count against warning and critical thresholds */
169 result = get_status((double)users, thlds);
170 117
171 if (result == STATE_UNKNOWN) 118 mp_perfdata users_pd = {
172 printf("%s\n", _("Unable to read output")); 119 .label = "users",
173 else { 120 .value = mp_create_pd_value(user_wrapper.users),
174 printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, 121 };
175 sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); 122
123 users_pd = mp_pd_set_thresholds(users_pd, config.thresholds);
124 mp_add_perfdata_to_subcheck(&sc_users, users_pd);
125
126 int tmp_status = mp_get_pd_status(users_pd);
127 sc_users = mp_set_subcheck_state(sc_users, tmp_status);
128
129 switch (tmp_status) {
130 case STATE_WARNING:
131 xasprintf(&sc_users.output,
132 "%d users currently logged in. This violates the warning threshold",
133 user_wrapper.users);
134 break;
135 case STATE_CRITICAL:
136 xasprintf(&sc_users.output,
137 "%d users currently logged in. This violates the critical threshold",
138 user_wrapper.users);
139 break;
140 default:
141 xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users);
176 } 142 }
177 143
178 return result; 144 mp_add_subcheck_to_check(&overall, sc_users);
145 mp_exit(overall);
179} 146}
180 147
148#define output_format_index CHAR_MAX + 1
149
181/* process command-line arguments */ 150/* process command-line arguments */
182int process_arguments(int argc, char **argv) { 151check_users_config_wrapper process_arguments(int argc, char **argv) {
183 static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, 152 static struct option longopts[] = {{"critical", required_argument, 0, 'c'},
184 {"warning", required_argument, 0, 'w'}, 153 {"warning", required_argument, 0, 'w'},
185 {"version", no_argument, 0, 'V'}, 154 {"version", no_argument, 0, 'V'},
186 {"help", no_argument, 0, 'h'}, 155 {"help", no_argument, 0, 'h'},
156 {"output-format", required_argument, 0, output_format_index},
187 {0, 0, 0, 0}}; 157 {0, 0, 0, 0}};
188 158
189 if (argc < 2) 159 if (argc < 2) {
190 usage("\n"); 160 usage(progname);
161 }
162
163 char *warning_range = NULL;
164 char *critical_range = NULL;
165 check_users_config_wrapper result = {
166 .config = check_users_config_init(),
167 .errorcode = OK,
168 };
191 169
192 int option_char;
193 while (true) { 170 while (true) {
194 int option = 0; 171 int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL);
195 option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option);
196 172
197 if (option_char == -1 || option_char == EOF || option_char == 1) 173 if (counter == -1 || counter == EOF || counter == 1) {
198 break; 174 break;
175 }
199 176
200 switch (option_char) { 177 switch (counter) {
201 case '?': /* print short usage statement if args not parsable */ 178 case '?': /* print short usage statement if args not parsable */
202 usage5(); 179 usage5();
203 case 'h': /* help */ 180 case 'h': /* help */
@@ -212,29 +189,66 @@ int process_arguments(int argc, char **argv) {
212 case 'w': /* warning */ 189 case 'w': /* warning */
213 warning_range = optarg; 190 warning_range = optarg;
214 break; 191 break;
192 case output_format_index: {
193 parsed_output_format parser = mp_parse_output_format(optarg);
194 if (!parser.parsing_success) {
195 // TODO List all available formats here, maybe add anothoer usage function
196 printf("Invalid output format: %s\n", optarg);
197 exit(STATE_UNKNOWN);
198 }
199
200 result.config.output_format_is_set = true;
201 result.config.output_format = parser.output_format;
202 break;
203 }
215 } 204 }
216 } 205 }
217 206
218 option_char = optind; 207 int option_char = optind;
219 208
220 if (warning_range == NULL && argc > option_char) 209 if (warning_range == NULL && argc > option_char) {
221 warning_range = argv[option_char++]; 210 warning_range = argv[option_char++];
211 }
222 212
223 if (critical_range == NULL && argc > option_char) 213 if (critical_range == NULL && argc > option_char) {
224 critical_range = argv[option_char++]; 214 critical_range = argv[option_char++];
215 }
225 216
226 /* this will abort in case of invalid ranges */ 217 // TODO add proper verification for ranges here!
227 set_thresholds(&thlds, warning_range, critical_range); 218 mp_range_parsed tmp;
219 if (warning_range) {
220 tmp = mp_parse_range_string(warning_range);
221 } else {
222 printf("Warning threshold missing\n");
223 print_usage();
224 exit(STATE_UNKNOWN);
225 }
228 226
229 if (!thlds->warning) { 227 if (tmp.error == MP_PARSING_SUCCES) {
230 usage4(_("Warning threshold must be a valid range expression")); 228 result.config.thresholds.warning = tmp.range;
229 result.config.thresholds.warning_is_set = true;
230 } else {
231 printf("Failed to parse warning range: %s", warning_range);
232 exit(STATE_UNKNOWN);
231 } 233 }
232 234
233 if (!thlds->critical) { 235 if (critical_range) {
234 usage4(_("Critical threshold must be a valid range expression")); 236 tmp = mp_parse_range_string(critical_range);
237 } else {
238 printf("Critical threshold missing\n");
239 print_usage();
240 exit(STATE_UNKNOWN);
235 } 241 }
236 242
237 return OK; 243 if (tmp.error == MP_PARSING_SUCCES) {
244 result.config.thresholds.critical = tmp.range;
245 result.config.thresholds.critical_is_set = true;
246 } else {
247 printf("Failed to parse critical range: %s", critical_range);
248 exit(STATE_UNKNOWN);
249 }
250
251 return result;
238} 252}
239 253
240void print_help(void) { 254void print_help(void) {
@@ -244,7 +258,8 @@ void print_help(void) {
244 printf(COPYRIGHT, copyright, email); 258 printf(COPYRIGHT, copyright, email);
245 259
246 printf("%s\n", _("This plugin checks the number of users currently logged in on the local")); 260 printf("%s\n", _("This plugin checks the number of users currently logged in on the local"));
247 printf("%s\n", _("system and generates an error if the number exceeds the thresholds specified.")); 261 printf("%s\n",
262 _("system and generates an error if the number exceeds the thresholds specified."));
248 263
249 printf("\n\n"); 264 printf("\n\n");
250 265
@@ -254,9 +269,12 @@ void print_help(void) {
254 printf(UT_EXTRA_OPTS); 269 printf(UT_EXTRA_OPTS);
255 270
256 printf(" %s\n", "-w, --warning=RANGE_EXPRESSION"); 271 printf(" %s\n", "-w, --warning=RANGE_EXPRESSION");
257 printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); 272 printf(" %s\n",
273 _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION"));
258 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); 274 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION");
259 printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); 275 printf(" %s\n",
276 _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION"));
277 printf(UT_OUTPUT_FORMAT);
260 278
261 printf(UT_SUPPORT); 279 printf(UT_SUPPORT);
262} 280}
diff --git a/plugins/check_users.d/config.h b/plugins/check_users.d/config.h
new file mode 100644
index 00000000..26d3ee70
--- /dev/null
+++ b/plugins/check_users.d/config.h
@@ -0,0 +1,20 @@
1#pragma once
2
3#include "output.h"
4#include "thresholds.h"
5
6typedef struct check_users_config {
7 mp_thresholds thresholds;
8
9 bool output_format_is_set;
10 mp_output_format output_format;
11} check_users_config;
12
13check_users_config check_users_config_init() {
14 check_users_config tmp = {
15 .thresholds = mp_thresholds_init(),
16
17 .output_format_is_set = false,
18 };
19 return tmp;
20}
diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c
new file mode 100644
index 00000000..f37819b1
--- /dev/null
+++ b/plugins/check_users.d/users.c
@@ -0,0 +1,169 @@
1#include "./users.h"
2
3#ifdef _WIN32
4# ifdef HAVE_WTSAPI32_H
5# include <windows.h>
6# include <wtsapi32.h>
7# undef ERROR
8# define ERROR -1
9
10get_num_of_users_wrapper get_num_of_users_windows() {
11 WTS_SESSION_INFO *wtsinfo;
12 DWORD wtscount;
13
14 get_num_of_users_wrapper result = {};
15
16 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) {
17 // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
18 result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS;
19 return result;
20 }
21
22 for (DWORD index = 0; index < wtscount; index++) {
23 LPTSTR username;
24 DWORD size;
25
26 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId,
27 WTSUserName, &username, &size)) {
28 continue;
29 }
30
31 int len = lstrlen(username);
32
33 WTSFreeMemory(username);
34
35 if (len == 0) {
36 continue;
37 }
38
39 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) {
40 result.users++;
41 }
42 }
43
44 WTSFreeMemory(wtsinfo);
45 return result;
46}
47# else // HAVE_WTSAPI32_H
48# error On windows but without the WTSAPI32 lib
49# endif // HAVE_WTSAPI32_H
50
51#else // _WIN32
52
53# include "../../config.h"
54# include <stddef.h>
55
56# ifdef HAVE_LIBSYSTEMD
57# include <systemd/sd-daemon.h>
58# include <systemd/sd-login.h>
59
60get_num_of_users_wrapper get_num_of_users_systemd() {
61 get_num_of_users_wrapper result = {};
62
63 // Test whether we booted with systemd
64 if (sd_booted() > 0) {
65 int users = sd_get_uids(NULL);
66 if (users >= 0) {
67 // Success
68 result.users = users;
69 return result;
70 }
71
72 // Failure! return the error code
73 result.errorcode = users;
74 return result;
75 }
76
77 // Looks like we are not running systemd,
78 // return with error here
79 result.errorcode = NO_SYSTEMD_ERROR;
80 return result;
81}
82# endif
83
84# ifdef HAVE_UTMPX_H
85# include <utmpx.h>
86
87get_num_of_users_wrapper get_num_of_users_utmp() {
88 int users = 0;
89
90 /* get currently logged users from utmpx */
91 setutxent();
92
93 struct utmpx *putmpx;
94 while ((putmpx = getutxent()) != NULL) {
95 if (putmpx->ut_type == USER_PROCESS) {
96 users++;
97 }
98 }
99
100 endutxent();
101
102 get_num_of_users_wrapper result = {
103 .errorcode = 0,
104 .users = users,
105 };
106
107 return result;
108}
109# endif
110
111# ifndef HAVE_WTSAPI32_H
112# ifndef HAVE_LIBSYSTEMD
113# ifndef HAVE_UTMPX_H
114// Fall back option here for the others (probably still not on windows)
115
116# include "../popen.h"
117# include "../common.h"
118# include "../utils.h"
119
120get_num_of_users_wrapper get_num_of_users_who_command() {
121 /* run the command */
122 child_process = spopen(WHO_COMMAND);
123 if (child_process == NULL) {
124 // printf(_("Could not open pipe: %s\n"), WHO_COMMAND);
125 get_num_of_users_wrapper result = {
126 .errorcode = COULD_NOT_OPEN_PIPE,
127 };
128 return result;
129 }
130
131 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
132 if (child_stderr == NULL) {
133 // printf(_("Could not open stderr for %s\n"), WHO_COMMAND);
134 // TODO this error should probably be reported
135 }
136
137 get_num_of_users_wrapper result = {};
138 char input_buffer[MAX_INPUT_BUFFER];
139 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
140 /* increment 'users' on all lines except total user count */
141 if (input_buffer[0] != '#') {
142 result.users++;
143 continue;
144 }
145
146 /* get total logged in users */
147 if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) {
148 break;
149 }
150 }
151
152 /* check STDERR */
153 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
154 // if this fails, something broke and the result can not be relied upon or so is the theorie
155 // here
156 result.errorcode = STDERR_COULD_NOT_BE_READ;
157 }
158 (void)fclose(child_stderr);
159
160 /* close the pipe */
161 spclose(child_process);
162
163 return result;
164}
165
166# endif
167# endif
168# endif
169#endif
diff --git a/plugins/check_users.d/users.h b/plugins/check_users.d/users.h
new file mode 100644
index 00000000..aacba775
--- /dev/null
+++ b/plugins/check_users.d/users.h
@@ -0,0 +1,18 @@
1#pragma once
2
3typedef struct get_num_of_users_wrapper {
4 int errorcode;
5 int users;
6} get_num_of_users_wrapper;
7
8enum {
9 NO_SYSTEMD_ERROR = 64,
10 WINDOWS_COULD_NOT_ENUMERATE_SESSIONS,
11 COULD_NOT_OPEN_PIPE,
12 STDERR_COULD_NOT_BE_READ,
13};
14
15get_num_of_users_wrapper get_num_of_users_systemd();
16get_num_of_users_wrapper get_num_of_users_utmp();
17get_num_of_users_wrapper get_num_of_users_windows();
18get_num_of_users_wrapper get_num_of_users_who_command();
diff --git a/plugins/common.h b/plugins/common.h
index b7a7d59b..ef888d08 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -1,120 +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 HAVE_SYS_TIME_H 95#ifdef HAVE_SYS_TIME_H
94# include <sys/time.h> 96# include <sys/time.h>
95#endif 97#endif
96#include <time.h> 98#include <time.h>
97 99
98#ifdef HAVE_SYS_TYPES_H 100#ifdef HAVE_SYS_TYPES_H
99#include <sys/types.h> 101# include <sys/types.h>
100#endif 102#endif
101 103
102#ifdef HAVE_SYS_SOCKET_H 104#ifdef HAVE_SYS_SOCKET_H
103#include <sys/socket.h> 105# include <sys/socket.h>
104#endif 106#endif
105 107
106#ifdef HAVE_SIGNAL_H 108#ifdef HAVE_SIGNAL_H
107#include <signal.h> 109# include <signal.h>
108#endif 110#endif
109 111
110/* GNU Libraries */ 112/* GNU Libraries */
111#include <getopt.h> 113#include <getopt.h>
112#include "dirname.h" 114#include "../gl/dirname.h"
113 115
114#include <locale.h> 116#include <locale.h>
115 117
116#ifdef HAVE_SYS_POLL_H 118#ifdef HAVE_SYS_POLL_H
117# include "sys/poll.h" 119# include "sys/poll.h"
118#endif 120#endif
119 121
120/* 122/*
@@ -124,42 +126,42 @@
124 */ 126 */
125 127
126#ifndef HAVE_STRTOL 128#ifndef HAVE_STRTOL
127# define strtol(a,b,c) atol((a)) 129# define strtol(a, b, c) atol((a))
128#endif 130#endif
129 131
130#ifndef HAVE_STRTOUL 132#ifndef HAVE_STRTOUL
131# define strtoul(a,b,c) (unsigned long)atol((a)) 133# define strtoul(a, b, c) (unsigned long)atol((a))
132#endif 134#endif
133 135
134/* SSL implementations */ 136/* SSL implementations */
135#ifdef HAVE_GNUTLS_OPENSSL_H 137#ifdef HAVE_GNUTLS_OPENSSL_H
136# include <gnutls/openssl.h> 138# include <gnutls/openssl.h>
137#else 139#else
138# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */ 140# define OPENSSL_LOAD_CONF /* See the OPENSSL_config(3) man page. */
139# ifdef HAVE_SSL_H 141# ifdef HAVE_SSL_H
140# include <rsa.h> 142# include <rsa.h>
141# include <crypto.h> 143# include <crypto.h>
142# include <x509.h> 144# include <x509.h>
143# include <pem.h> 145# include <pem.h>
144# include <ssl.h> 146# include <ssl.h>
145# include <err.h> 147# include <err.h>
146# else 148# else
147# ifdef HAVE_OPENSSL_SSL_H 149# ifdef HAVE_OPENSSL_SSL_H
148# include <openssl/rsa.h> 150# include <openssl/rsa.h>
149# include <openssl/crypto.h> 151# include <openssl/crypto.h>
150# include <openssl/x509.h> 152# include <openssl/x509.h>
151# include <openssl/pem.h> 153# include <openssl/pem.h>
152# include <openssl/ssl.h> 154# include <openssl/ssl.h>
153# include <openssl/err.h> 155# include <openssl/err.h>
154# endif 156# endif
155# endif 157# endif
156#endif 158#endif
157 159
158/* 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 */
159#ifdef OPENSSL_VERSION_NUMBER 161#ifdef OPENSSL_VERSION_NUMBER
160# if OPENSSL_VERSION_NUMBER >= 0x10100000 162# if OPENSSL_VERSION_NUMBER >= 0x10100000
161# define OPENSSL_NO_SSL2 163# define OPENSSL_NO_SSL2
162# endif 164# endif
163#endif 165#endif
164 166
165/* 167/*
@@ -170,7 +172,7 @@
170 172
171/* MariaDB 10.2 client does not set MYSQL_PORT */ 173/* MariaDB 10.2 client does not set MYSQL_PORT */
172#ifndef MYSQL_PORT 174#ifndef MYSQL_PORT
173# define MYSQL_PORT 3306 175# define MYSQL_PORT 3306
174#endif 176#endif
175 177
176enum { 178enum {
@@ -179,17 +181,9 @@ enum {
179}; 181};
180 182
181enum { 183enum {
182 STATE_OK, 184 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
183 STATE_WARNING, 185 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
184 STATE_CRITICAL, 186 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
185 STATE_UNKNOWN,
186 STATE_DEPENDENT
187};
188
189enum {
190 DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
191 MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
192 MAX_HOST_ADDRESS_LENGTH = 256 /* max size of a host address */
193}; 187};
194 188
195/* 189/*
@@ -197,18 +191,18 @@ enum {
197 * Internationalization 191 * Internationalization
198 * 192 *
199 */ 193 */
200#include "gettext.h" 194#include "../gl/gettext.h"
201#define _(String) gettext (String) 195#define _(String) gettext(String)
202#if ! ENABLE_NLS 196#if !ENABLE_NLS
203# undef textdomain 197# undef textdomain
204# define textdomain(Domainname) /* empty */ 198# define textdomain(Domainname) /* empty */
205# undef bindtextdomain 199# undef bindtextdomain
206# define bindtextdomain(Domainname, Dirname) /* empty */ 200# define bindtextdomain(Domainname, Dirname) /* empty */
207#endif 201#endif
208 202
209/* For non-GNU compilers to ignore __attribute__ */ 203/* For non-GNU compilers to ignore __attribute__ */
210#ifndef __GNUC__ 204#ifndef __GNUC__
211# define __attribute__(x) /* do nothing */ 205# define __attribute__(x) /* do nothing */
212#endif 206#endif
213 207
214#endif /* _COMMON_H_ */ 208#endif /* _COMMON_H_ */
diff --git a/plugins/negate.c b/plugins/negate.c
index 7e52fe67..a42a6c59 100644
--- a/plugins/negate.c
+++ b/plugins/negate.c
@@ -38,21 +38,18 @@ const char *email = "devel@monitoring-plugins.org";
38#include "common.h" 38#include "common.h"
39#include "utils.h" 39#include "utils.h"
40#include "utils_cmd.h" 40#include "utils_cmd.h"
41#include "negate.d/config.h"
42#include "../lib/states.h"
41 43
42#include <ctype.h> 44typedef struct {
45 int errorcode;
46 negate_config config;
47} negate_config_wrapper;
48static negate_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
49static negate_config_wrapper validate_arguments(negate_config_wrapper /*config_wrapper*/);
43 50
44static const char **process_arguments(int /*argc*/, char ** /*argv*/);
45static void validate_arguments(char ** /*command_line*/);
46static void print_help(void); 51static void print_help(void);
47void print_usage(void); 52void print_usage(void);
48static bool subst_text = false;
49
50static int state[4] = {
51 STATE_OK,
52 STATE_WARNING,
53 STATE_CRITICAL,
54 STATE_UNKNOWN,
55};
56 53
57int main(int argc, char **argv) { 54int main(int argc, char **argv) {
58 setlocale(LC_ALL, ""); 55 setlocale(LC_ALL, "");
@@ -61,15 +58,24 @@ int main(int argc, char **argv) {
61 58
62 timeout_interval = DEFAULT_TIMEOUT; 59 timeout_interval = DEFAULT_TIMEOUT;
63 60
64 char **command_line = (char **)process_arguments(argc, argv); 61 negate_config_wrapper tmp_config = process_arguments(argc, argv);
62
63 if (tmp_config.errorcode == ERROR) {
64 die(STATE_UNKNOWN, _("negate: Failed to parse input"));
65 }
66
67 negate_config config = tmp_config.config;
68
69 char **command_line = config.command_line;
65 70
66 /* Set signal handling and alarm */ 71 /* Set signal handling and alarm */
67 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) 72 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
68 die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); 73 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
74 }
69 75
70 (void)alarm((unsigned)timeout_interval); 76 (void)alarm(timeout_interval);
71 77
72 int result = STATE_UNKNOWN; 78 mp_state_enum result = STATE_UNKNOWN;
73 output chld_out; 79 output chld_out;
74 output chld_err; 80 output chld_err;
75 81
@@ -86,46 +92,54 @@ int main(int argc, char **argv) {
86 } 92 }
87 93
88 /* Return UNKNOWN or worse if no output is returned */ 94 /* Return UNKNOWN or worse if no output is returned */
89 if (chld_out.lines == 0) 95 if (chld_out.lines == 0) {
90 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n")); 96 die(max_state_alt(result, STATE_UNKNOWN), _("No data returned from command\n"));
97 }
91 98
92 char *sub; 99 char *sub;
93 for (size_t i = 0; i < chld_out.lines; i++) { 100 for (size_t i = 0; i < chld_out.lines; i++) {
94 if (subst_text && result >= 0 && result <= 4 && result != state[result]) { 101 if (config.subst_text && result >= 0 && result <= 4 && result != config.state[result]) {
95 /* Loop over each match found */ 102 /* Loop over each match found */
96 while ((sub = strstr(chld_out.line[i], state_text(result)))) { 103 while ((sub = strstr(chld_out.line[i], state_text(result)))) {
97 /* Terminate the first part and skip over the string we'll substitute */ 104 /* Terminate the first part and skip over the string we'll substitute */
98 *sub = '\0'; 105 *sub = '\0';
99 sub += strlen(state_text(result)); 106 sub += strlen(state_text(result));
100 /* then put everything back together */ 107 /* then put everything back together */
101 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i], state_text(state[result]), sub); 108 xasprintf(&chld_out.line[i], "%s%s%s", chld_out.line[i],
109 state_text(config.state[result]), sub);
102 } 110 }
103 } 111 }
104 printf("%s\n", chld_out.line[i]); 112 printf("%s\n", chld_out.line[i]);
105 } 113 }
106 114
107 if (result >= 0 && result <= 4) { 115 if (result >= 0 && result <= 4) {
108 exit(state[result]); 116 exit(config.state[result]);
109 } else { 117 } else {
110 exit(result); 118 exit(result);
111 } 119 }
112} 120}
113 121
114/* process command-line arguments */ 122/* process command-line arguments */
115static const char **process_arguments(int argc, char **argv) { 123static negate_config_wrapper process_arguments(int argc, char **argv) {
116 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, 124 static struct option longopts[] = {
117 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'}, 125 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'},
118 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'}, 126 {"timeout", required_argument, 0, 't'}, {"timeout-result", required_argument, 0, 'T'},
119 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'}, 127 {"ok", required_argument, 0, 'o'}, {"warning", required_argument, 0, 'w'},
120 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}}; 128 {"critical", required_argument, 0, 'c'}, {"unknown", required_argument, 0, 'u'},
121 129 {"substitute", no_argument, 0, 's'}, {0, 0, 0, 0}};
130
131 negate_config_wrapper result = {
132 .errorcode = OK,
133 .config = negate_config_init(),
134 };
122 bool permute = true; 135 bool permute = true;
123 while (true) { 136 while (true) {
124 int option = 0; 137 int option = 0;
125 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option); 138 int option_char = getopt_long(argc, argv, "+hVt:T:o:w:c:u:s", longopts, &option);
126 139
127 if (option_char == -1 || option_char == EOF) 140 if (option_char == -1 || option_char == EOF) {
128 break; 141 break;
142 }
129 143
130 switch (option_char) { 144 switch (option_char) {
131 case '?': /* help */ 145 case '?': /* help */
@@ -133,64 +147,80 @@ static const char **process_arguments(int argc, char **argv) {
133 break; 147 break;
134 case 'h': /* help */ 148 case 'h': /* help */
135 print_help(); 149 print_help();
136 exit(EXIT_SUCCESS); 150 exit(STATE_UNKNOWN);
137 break; 151 break;
138 case 'V': /* version */ 152 case 'V': /* version */
139 print_revision(progname, NP_VERSION); 153 print_revision(progname, NP_VERSION);
140 exit(EXIT_SUCCESS); 154 exit(STATE_UNKNOWN);
141 case 't': /* timeout period */ 155 case 't': /* timeout period */
142 if (!is_integer(optarg)) 156 if (!is_integer(optarg)) {
143 usage2(_("Timeout interval must be a positive integer"), optarg); 157 usage2(_("Timeout interval must be a positive integer"), optarg);
144 else 158 } else {
145 timeout_interval = atoi(optarg); 159 timeout_interval = atoi(optarg);
160 }
146 break; 161 break;
147 case 'T': /* Result to return on timeouts */ 162 case 'T': /* Result to return on timeouts */
148 if ((timeout_state = mp_translate_state(optarg)) == ERROR) 163 if ((timeout_state = mp_translate_state(optarg)) == ERROR) {
149 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 164 usage4(_("Timeout result must be a valid state name (OK, WARNING, CRITICAL, "
165 "UNKNOWN) or integer (0-3)."));
166 }
150 break; 167 break;
151 case 'o': /* replacement for OK */ 168 case 'o': /* replacement for OK */
152 if ((state[STATE_OK] = mp_translate_state(optarg)) == ERROR) 169 if ((result.config.state[STATE_OK] = mp_translate_state(optarg)) == ERROR) {
153 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 170 usage4(_("Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
171 "integer (0-3)."));
172 }
154 permute = false; 173 permute = false;
155 break; 174 break;
156 175
157 case 'w': /* replacement for WARNING */ 176 case 'w': /* replacement for WARNING */
158 if ((state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) 177 if ((result.config.state[STATE_WARNING] = mp_translate_state(optarg)) == ERROR) {
159 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 178 usage4(_("Warning must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
179 "integer (0-3)."));
180 }
160 permute = false; 181 permute = false;
161 break; 182 break;
162 case 'c': /* replacement for CRITICAL */ 183 case 'c': /* replacement for CRITICAL */
163 if ((state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) 184 if ((result.config.state[STATE_CRITICAL] = mp_translate_state(optarg)) == ERROR) {
164 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 185 usage4(_("Critical must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
186 "integer (0-3)."));
187 }
165 permute = false; 188 permute = false;
166 break; 189 break;
167 case 'u': /* replacement for UNKNOWN */ 190 case 'u': /* replacement for UNKNOWN */
168 if ((state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) 191 if ((result.config.state[STATE_UNKNOWN] = mp_translate_state(optarg)) == ERROR) {
169 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); 192 usage4(_("Unknown must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or "
193 "integer (0-3)."));
194 }
170 permute = false; 195 permute = false;
171 break; 196 break;
172 case 's': /* Substitute status text */ 197 case 's': /* Substitute status text */
173 subst_text = true; 198 result.config.subst_text = true;
174 break; 199 break;
175 } 200 }
176 } 201 }
177 202
178 validate_arguments(&argv[optind]);
179
180 if (permute) { /* No [owcu] switch specified, default to this */ 203 if (permute) { /* No [owcu] switch specified, default to this */
181 state[STATE_OK] = STATE_CRITICAL; 204 result.config.state[STATE_OK] = STATE_CRITICAL;
182 state[STATE_CRITICAL] = STATE_OK; 205 result.config.state[STATE_CRITICAL] = STATE_OK;
183 } 206 }
184 207
185 return (const char **)&argv[optind]; 208 result.config.command_line = &argv[optind];
209
210 return validate_arguments(result);
186} 211}
187 212
188void validate_arguments(char **command_line) { 213negate_config_wrapper validate_arguments(negate_config_wrapper config_wrapper) {
189 if (command_line[0] == NULL) 214 if (config_wrapper.config.command_line[0] == NULL) {
190 usage4(_("Could not parse arguments")); 215 usage4(_("Could not parse arguments"));
216 }
191 217
192 if (strncmp(command_line[0], "/", 1) != 0 && strncmp(command_line[0], "./", 2) != 0) 218 if (strncmp(config_wrapper.config.command_line[0], "/", 1) != 0 &&
219 strncmp(config_wrapper.config.command_line[0], "./", 2) != 0) {
193 usage4(_("Require path to command")); 220 usage4(_("Require path to command"));
221 }
222
223 return config_wrapper;
194} 224}
195 225
196void print_help(void) { 226void print_help(void) {
@@ -198,7 +228,8 @@ void print_help(void) {
198 228
199 printf(COPYRIGHT, copyright, email); 229 printf(COPYRIGHT, copyright, email);
200 230
201 printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and vice-versa) by default.")); 231 printf("%s\n", _("Negates only the return code of a plugin (returns OK for CRITICAL and "
232 "vice-versa) by default."));
202 printf("%s\n", _("Additional switches can be used to control:\n")); 233 printf("%s\n", _("Additional switches can be used to control:\n"));
203 printf("\t - which state becomes what\n"); 234 printf("\t - which state becomes what\n");
204 printf("\t - changing the plugin output text to match the return code"); 235 printf("\t - changing the plugin output text to match the return code");
@@ -228,17 +259,20 @@ void print_help(void) {
228 printf("%s\n", _("Examples:")); 259 printf("%s\n", _("Examples:"));
229 printf(" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host"); 260 printf(" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host");
230 printf(" %s\n", _("Run check_ping and invert result. Must use full path to plugin")); 261 printf(" %s\n", _("Run check_ping and invert result. Must use full path to plugin"));
231 printf(" %s\n", "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'"); 262 printf(" %s\n",
263 "negate -w OK -c UNKNOWN /usr/local/nagios/libexec/check_procs -a 'vi negate.c'");
232 printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL")); 264 printf(" %s\n", _("This will return OK instead of WARNING and UNKNOWN instead of CRITICAL"));
233 printf("\n"); 265 printf("\n");
234 printf("%s\n", _("Notes:")); 266 printf("%s\n", _("Notes:"));
235 printf(" %s\n", _("This plugin is a wrapper to take the output of another plugin and invert it.")); 267 printf(" %s\n",
268 _("This plugin is a wrapper to take the output of another plugin and invert it."));
236 printf(" %s\n", _("The full path of the plugin must be provided.")); 269 printf(" %s\n", _("The full path of the plugin must be provided."));
237 printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL.")); 270 printf(" %s\n", _("If the wrapped plugin returns OK, the wrapper will return CRITICAL."));
238 printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK.")); 271 printf(" %s\n", _("If the wrapped plugin returns CRITICAL, the wrapper will return OK."));
239 printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged.")); 272 printf(" %s\n", _("Otherwise, the output state of the wrapped plugin is unchanged."));
240 printf("\n"); 273 printf("\n");
241 printf(" %s\n", _("Using timeout-result, it is possible to override the timeout behaviour or a")); 274 printf(" %s\n",
275 _("Using timeout-result, it is possible to override the timeout behaviour or a"));
242 printf(" %s\n", _("plugin by setting the negate timeout a bit lower.")); 276 printf(" %s\n", _("plugin by setting the negate timeout a bit lower."));
243 277
244 printf(UT_SUPPORT); 278 printf(UT_SUPPORT);
diff --git a/plugins/negate.d/config.h b/plugins/negate.d/config.h
new file mode 100644
index 00000000..0cf30cd4
--- /dev/null
+++ b/plugins/negate.d/config.h
@@ -0,0 +1,24 @@
1#pragma once
2
3#include "states.h"
4
5typedef struct {
6 mp_state_enum state[4];
7 bool subst_text;
8 char **command_line;
9} negate_config;
10
11negate_config negate_config_init() {
12 negate_config tmp = {
13 .state =
14 {
15 STATE_OK,
16 STATE_WARNING,
17 STATE_CRITICAL,
18 STATE_UNKNOWN,
19 },
20 .subst_text = false,
21 .command_line = NULL,
22 };
23 return tmp;
24}
diff --git a/plugins/netutils.c b/plugins/netutils.c
index ee81912a..b4c6ff0a 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -28,13 +28,16 @@
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
@@ -43,119 +46,129 @@ int address_family = AF_INET;
43 46
44/* handles socket timeouts */ 47/* handles socket timeouts */
45void socket_timeout_alarm_handler(int sig) { 48void socket_timeout_alarm_handler(int sig) {
46 if (sig == SIGALRM) 49 mp_subcheck timeout_sc = mp_subcheck_init();
47 printf(_("%s - Socket timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 50 timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state);
48 else 51
49 printf(_("%s - Abnormal timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 52 if (sig == SIGALRM) {
53 xasprintf(&timeout_sc.output, _("Socket timeout after %d seconds\n"), socket_timeout);
54 } else {
55 xasprintf(&timeout_sc.output, _("Abnormal timeout after %d seconds\n"), socket_timeout);
56 }
50 57
51 exit(socket_timeout_state); 58 mp_check overall = mp_check_init();
59 mp_add_subcheck_to_check(&overall, timeout_sc);
60
61 mp_exit(overall);
52} 62}
53 63
54/* 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
55 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
56 multi-packet answer */ 66 multi-packet answer */
57int process_tcp_request2(const char *server_address, int server_port, const char *send_buffer, char *recv_buffer, int recv_size) { 67mp_state_enum process_tcp_request2(const char *server_address, const int server_port,
68 const char *send_buffer, char *recv_buffer,
69 const int recv_size) {
58 70
59 int result; 71 int socket;
60 int send_result;
61 int recv_result;
62 int sd;
63 struct timeval tv;
64 fd_set readfds;
65 int recv_length = 0;
66 72
67 result = np_net_connect(server_address, server_port, &sd, IPPROTO_TCP); 73 mp_state_enum connect_result =
68 if (result != STATE_OK) 74 np_net_connect(server_address, server_port, &socket, IPPROTO_TCP);
75 if (connect_result != STATE_OK) {
69 return STATE_CRITICAL; 76 return STATE_CRITICAL;
77 }
70 78
71 send_result = send(sd, send_buffer, strlen(send_buffer), 0); 79 mp_state_enum result;
80 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
72 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { 81 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
73 printf("%s\n", _("Send failed")); 82 // printf("%s\n", _("Send failed"));
74 result = STATE_WARNING; 83 result = STATE_WARNING;
75 } 84 }
76 85
77 while (1) { 86 fd_set readfds;
87 ssize_t recv_length = 0;
88 while (true) {
78 /* wait up to the number of seconds for socket timeout 89 /* wait up to the number of seconds for socket timeout
79 minus one for data from the host */ 90 minus one for data from the host */
80 tv.tv_sec = socket_timeout - 1; 91 struct timeval timeout = {
81 tv.tv_usec = 0; 92 .tv_sec = socket_timeout - 1,
93 .tv_usec = 0,
94 };
82 FD_ZERO(&readfds); 95 FD_ZERO(&readfds);
83 FD_SET(sd, &readfds); 96 FD_SET(socket, &readfds);
84 select(sd + 1, &readfds, NULL, NULL, &tv); 97 select(socket + 1, &readfds, NULL, NULL, &timeout);
85 98
86 /* make sure some data has arrived */ 99 /* make sure some data has arrived */
87 if (!FD_ISSET(sd, &readfds)) { /* it hasn't */ 100 if (!FD_ISSET(socket, &readfds)) { /* it hasn't */
88 if (!recv_length) { 101 if (!recv_length) {
89 strcpy(recv_buffer, ""); 102 strcpy(recv_buffer, "");
90 printf("%s\n", _("No data was received from host!")); 103 // printf("%s\n", _("No data was received from host!"));
91 result = STATE_WARNING; 104 result = STATE_WARNING;
92 } else { /* this one failed, but previous ones worked */ 105 } else { /* this one failed, but previous ones worked */
93 recv_buffer[recv_length] = 0; 106 recv_buffer[recv_length] = 0;
94 } 107 }
95 break; 108 break;
96 } else { /* it has */ 109 } /* it has */
97 recv_result = recv(sd, recv_buffer + recv_length, (size_t)recv_size - recv_length - 1, 0); 110
98 if (recv_result == -1) { 111 ssize_t recv_result =
99 /* recv failed, bail out */ 112 recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0);
100 strcpy(recv_buffer + recv_length, ""); 113 if (recv_result == -1) {
101 result = STATE_WARNING; 114 /* recv failed, bail out */
102 break; 115 strcpy(recv_buffer + recv_length, "");
103 } else if (recv_result == 0) { 116 result = STATE_WARNING;
104 /* end of file ? */ 117 break;
105 recv_buffer[recv_length] = 0; 118 }
106 break; 119
107 } else { /* we got data! */ 120 if (recv_result == 0) {
108 recv_length += recv_result; 121 /* end of file ? */
109 if (recv_length >= recv_size - 1) { 122 recv_buffer[recv_length] = 0;
110 /* buffer full, we're done */ 123 break;
111 recv_buffer[recv_size - 1] = 0; 124 }
112 break; 125
113 } 126 /* we got data! */
114 } 127 recv_length += recv_result;
128 if (recv_length >= recv_size - 1) {
129 /* buffer full, we're done */
130 recv_buffer[recv_size - 1] = 0;
131 break;
115 } 132 }
116 /* end if(!FD_ISSET(sd,&readfds)) */ 133 /* end if(!FD_ISSET(sd,&readfds)) */
117 } 134 }
118 /* end while(1) */
119 135
120 close(sd); 136 close(socket);
121 return result; 137 return result;
122} 138}
123 139
124/* 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
125 response */ 141 response */
126int process_request(const char *server_address, int server_port, int proto, const char *send_buffer, char *recv_buffer, int recv_size) { 142mp_state_enum process_request(const char *server_address, const int server_port, const int proto,
127 int result; 143 const char *send_buffer, char *recv_buffer, const int recv_size) {
128 int sd;
129 144
130 result = STATE_OK; 145 mp_state_enum result = STATE_OK;
131 146 int socket;
132 result = np_net_connect(server_address, server_port, &sd, proto); 147 result = np_net_connect(server_address, server_port, &socket, proto);
133 if (result != STATE_OK) 148 if (result != STATE_OK) {
134 return STATE_CRITICAL; 149 return STATE_CRITICAL;
150 }
135 151
136 result = send_request(sd, proto, send_buffer, recv_buffer, recv_size); 152 result = send_request(socket, proto, send_buffer, recv_buffer, recv_size);
137 153
138 close(sd); 154 close(socket);
139 155
140 return result; 156 return result;
141} 157}
142 158
143/* 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 */
144int np_net_connect(const char *host_name, int port, int *sd, int proto) { 160mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor,
161 const int proto) {
145 /* send back STATE_UNKOWN if there's an error 162 /* send back STATE_UNKOWN if there's an error
146 send back STATE_OK if we connect 163 send back STATE_OK if we connect
147 send back STATE_CRITICAL if we can't connect. 164 send back STATE_CRITICAL if we can't connect.
148 Let upstream figure out what to send to the user. */ 165 Let upstream figure out what to send to the user. */
149 struct addrinfo hints; 166 bool is_socket = (host_name[0] == '/');
150 struct addrinfo *r, *res; 167 int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
151 struct sockaddr_un su;
152 char port_str[6], host[MAX_HOST_ADDRESS_LENGTH];
153 size_t len;
154 int socktype, result;
155 short is_socket = (host_name[0] == '/');
156
157 socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
158 168
169 struct addrinfo hints = {};
170 struct addrinfo *res = NULL;
171 int result;
159 /* 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 */
160 if (!is_socket) { 173 if (!is_socket) {
161 memset(&hints, 0, sizeof(hints)); 174 memset(&hints, 0, sizeof(hints));
@@ -163,37 +176,46 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
163 hints.ai_protocol = proto; 176 hints.ai_protocol = proto;
164 hints.ai_socktype = socktype; 177 hints.ai_socktype = socktype;
165 178
166 len = strlen(host_name); 179 size_t len = strlen(host_name);
167 /* check for an [IPv6] address (and strip the brackets) */ 180 /* check for an [IPv6] address (and strip the brackets) */
168 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { 181 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
169 host_name++; 182 host_name++;
170 len -= 2; 183 len -= 2;
171 } 184 }
172 if (len >= sizeof(host)) 185
186 char host[MAX_HOST_ADDRESS_LENGTH];
187
188 if (len >= sizeof(host)) {
173 return STATE_UNKNOWN; 189 return STATE_UNKNOWN;
190 }
191
174 memcpy(host, host_name, len); 192 memcpy(host, host_name, len);
175 host[len] = '\0'; 193 host[len] = '\0';
194
195 char port_str[6];
176 snprintf(port_str, sizeof(port_str), "%d", port); 196 snprintf(port_str, sizeof(port_str), "%d", port);
177 result = getaddrinfo(host, port_str, &hints, &res); 197 int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res);
178 198
179 if (result != 0) { 199 if (getaddrinfo_err != 0) {
180 printf("%s\n", gai_strerror(result)); 200 // printf("%s\n", gai_strerror(result));
181 return STATE_UNKNOWN; 201 return STATE_UNKNOWN;
182 } 202 }
183 203
184 r = res; 204 struct addrinfo *addressPointer = res;
185 while (r) { 205 while (addressPointer) {
186 /* attempt to create a socket */ 206 /* attempt to create a socket */
187 *sd = socket(r->ai_family, socktype, r->ai_protocol); 207 *socketDescriptor =
208 socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol);
188 209
189 if (*sd < 0) { 210 if (*socketDescriptor < 0) {
190 printf("%s\n", _("Socket creation failed")); 211 // printf("%s\n", _("Socket creation failed"));
191 freeaddrinfo(r); 212 freeaddrinfo(addressPointer);
192 return STATE_UNKNOWN; 213 return STATE_UNKNOWN;
193 } 214 }
194 215
195 /* attempt to open a connection */ 216 /* attempt to open a connection */
196 result = connect(*sd, r->ai_addr, r->ai_addrlen); 217 result =
218 connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen);
197 219
198 if (result == 0) { 220 if (result == 0) {
199 was_refused = false; 221 was_refused = false;
@@ -208,39 +230,48 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
208 } 230 }
209 } 231 }
210 232
211 close(*sd); 233 close(*socketDescriptor);
212 r = r->ai_next; 234 addressPointer = addressPointer->ai_next;
213 } 235 }
236
214 freeaddrinfo(res); 237 freeaddrinfo(res);
215 } 238
216 /* else the hostname is interpreted as a path to a unix socket */ 239 } else {
217 else { 240 /* else the hostname is interpreted as a path to a unix socket */
218 if (strlen(host_name) >= UNIX_PATH_MAX) { 241 if (strlen(host_name) >= UNIX_PATH_MAX) {
219 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); 242 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
220 } 243 }
221 memset(&su, 0, sizeof(su)); 244
245 struct sockaddr_un su = {};
222 su.sun_family = AF_UNIX; 246 su.sun_family = AF_UNIX;
223 strncpy(su.sun_path, host_name, UNIX_PATH_MAX); 247 strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
224 *sd = socket(PF_UNIX, SOCK_STREAM, 0); 248 *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0);
225 if (*sd < 0) { 249
250 if (*socketDescriptor < 0) {
226 die(STATE_UNKNOWN, _("Socket creation failed")); 251 die(STATE_UNKNOWN, _("Socket creation failed"));
227 } 252 }
228 result = connect(*sd, (struct sockaddr *)&su, sizeof(su)); 253
229 if (result < 0 && errno == ECONNREFUSED) 254 result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su));
255 if (result < 0 && errno == ECONNREFUSED) {
230 was_refused = true; 256 was_refused = true;
257 }
231 } 258 }
232 259
233 if (result == 0) 260 if (result == 0) {
234 return STATE_OK; 261 return STATE_OK;
235 else if (was_refused) { 262 }
263
264 if (was_refused) {
236 switch (econn_refuse_state) { /* a user-defined expected outcome */ 265 switch (econn_refuse_state) { /* a user-defined expected outcome */
237 case STATE_OK: 266 case STATE_OK:
238 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ 267 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
239 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 */
240 if (is_socket) 269 if (is_socket) {
241 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));
242 else 271 } else {
243 printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno)); 272 // printf("connect to address %s and port %d: %s\n", host_name, port,
273 // strerror(errno));
274 }
244 return STATE_CRITICAL; 275 return STATE_CRITICAL;
245 break; 276 break;
246 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) */
@@ -248,98 +279,108 @@ int np_net_connect(const char *host_name, int port, int *sd, int proto) {
248 break; 279 break;
249 } 280 }
250 } else { 281 } else {
251 if (is_socket) 282 if (is_socket) {
252 printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 283 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
253 else 284 } else {
254 printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno)); 285 // printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno));
286 }
255 return STATE_CRITICAL; 287 return STATE_CRITICAL;
256 } 288 }
257} 289}
258 290
259int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) { 291mp_state_enum send_request(const int socket, const int proto, const char *send_buffer,
260 int result = STATE_OK; 292 char *recv_buffer, const int recv_size) {
261 int send_result; 293 mp_state_enum result = STATE_OK;
262 int recv_result;
263 struct timeval tv;
264 fd_set readfds;
265 294
266 send_result = send(sd, send_buffer, strlen(send_buffer), 0); 295 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
267 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) { 296 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
268 printf("%s\n", _("Send failed")); 297 // printf("%s\n", _("Send failed"));
269 result = STATE_WARNING; 298 result = STATE_WARNING;
270 } 299 }
271 300
272 /* 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
273 for data from the host */ 302 for data from the host */
274 tv.tv_sec = socket_timeout - 1; 303 struct timeval timestamp = {
275 tv.tv_usec = 0; 304 .tv_sec = socket_timeout - 1,
305 .tv_usec = 0,
306 };
307 fd_set readfds;
276 FD_ZERO(&readfds); 308 FD_ZERO(&readfds);
277 FD_SET(sd, &readfds); 309 FD_SET(socket, &readfds);
278 select(sd + 1, &readfds, NULL, NULL, &tv); 310 select(socket + 1, &readfds, NULL, NULL, &timestamp);
279 311
280 /* make sure some data has arrived */ 312 /* make sure some data has arrived */
281 if (!FD_ISSET(sd, &readfds)) { 313 if (!FD_ISSET(socket, &readfds)) {
282 strcpy(recv_buffer, ""); 314 strcpy(recv_buffer, "");
283 printf("%s\n", _("No data was received from host!")); 315 // printf("%s\n", _("No data was received from host!"));
284 result = STATE_WARNING; 316 result = STATE_WARNING;
285 } 317 } else {
286 318 ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0);
287 else {
288 recv_result = recv(sd, recv_buffer, (size_t)recv_size - 1, 0);
289 if (recv_result == -1) { 319 if (recv_result == -1) {
290 strcpy(recv_buffer, ""); 320 strcpy(recv_buffer, "");
291 if (proto != IPPROTO_TCP) 321 if (proto != IPPROTO_TCP) {
292 printf("%s\n", _("Receive failed")); 322 // printf("%s\n", _("Receive failed"));
323 }
293 result = STATE_WARNING; 324 result = STATE_WARNING;
294 } else 325 } else {
295 recv_buffer[recv_result] = 0; 326 recv_buffer[recv_result] = 0;
327 }
296 328
297 /* die returned string */ 329 /* die returned string */
298 recv_buffer[recv_size - 1] = 0; 330 recv_buffer[recv_size - 1] = 0;
299 } 331 }
332
300 return result; 333 return result;
301} 334}
302 335
303bool is_host(const char *address) { 336bool is_host(const char *address) {
304 if (is_addr(address) || is_hostname(address)) 337 if (is_addr(address) || is_hostname(address)) {
305 return (true); 338 return (true);
339 }
306 340
307 return (false); 341 return (false);
308} 342}
309 343
310void host_or_die(const char *str) { 344void host_or_die(const char *str) {
311 if (!str || (!is_addr(str) && !is_hostname(str))) 345 if (!str || (!is_addr(str) && !is_hostname(str))) {
312 usage_va(_("Invalid hostname/address - %s"), str); 346 usage_va(_("Invalid hostname/address - %s"), str);
347 }
313} 348}
314 349
315bool is_addr(const char *address) { 350bool is_addr(const char *address) {
316#ifdef USE_IPV6 351#ifdef USE_IPV6
317 if (address_family == AF_INET && is_inet_addr(address)) 352 if (address_family == AF_INET && is_inet_addr(address)) {
318 return true; 353 return true;
319 else if (address_family == AF_INET6 && is_inet6_addr(address)) 354 }
355
356 if (address_family == AF_INET6 && is_inet6_addr(address)) {
320 return true; 357 return true;
358 }
321#else 359#else
322 if (is_inet_addr(address)) 360 if (is_inet_addr(address)) {
323 return (true); 361 return true;
362 }
324#endif 363#endif
325 364
326 return (false); 365 return false;
327} 366}
328 367
329int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) { 368bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) {
330 struct addrinfo hints; 369 struct addrinfo hints;
331 struct addrinfo *res;
332 int retval;
333
334 memset(&hints, 0, sizeof(struct addrinfo)); 370 memset(&hints, 0, sizeof(struct addrinfo));
335 hints.ai_family = family; 371 hints.ai_family = family;
336 372
337 retval = getaddrinfo(in, NULL, &hints, &res); 373 struct addrinfo *res;
338 if (retval != 0) 374 int retval = getaddrinfo(node_string, NULL, &hints, &res);
375 if (retval != 0) {
339 return false; 376 return false;
377 }
340 378
341 if (ss != NULL) 379 if (ss != NULL) {
342 memcpy(ss, res->ai_addr, res->ai_addrlen); 380 memcpy(ss, res->ai_addr, res->ai_addrlen);
381 }
382
343 freeaddrinfo(res); 383 freeaddrinfo(res);
384
344 return true; 385 return true;
345} 386}
diff --git a/plugins/netutils.h b/plugins/netutils.h
index a95057e0..c4461113 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -1,120 +1,120 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins net utilities include file 3 * Monitoring Plugins net utilities include file
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2007 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2007 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains common include files and function definitions 11 * This file contains common include files and function definitions
12* used in many of the plugins. 12 * used in many of the plugins.
13* 13 *
14* 14 *
15* This program is free software: you can redistribute it and/or modify 15 * This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16 * it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17 * the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18 * (at your option) any later version.
19* 19 *
20* This program is distributed in the hope that it will be useful, 20 * This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23 * GNU General Public License for more details.
24* 24 *
25* You should have received a copy of the GNU General Public License 25 * You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27 *
28* 28 *
29*****************************************************************************/ 29 *****************************************************************************/
30 30
31#ifndef _NETUTILS_H_ 31#ifndef _NETUTILS_H_
32#define _NETUTILS_H_ 32#define _NETUTILS_H_
33 33
34#include "common.h" 34#include "output.h"
35#include "states.h"
35#include "utils.h" 36#include "utils.h"
36#include <netinet/in.h> 37#include <netinet/in.h>
37#include <arpa/inet.h> 38#include <arpa/inet.h>
38#include <netdb.h> 39#include <netdb.h>
39 40
40#ifdef HAVE_SYS_UN_H 41#ifdef HAVE_SYS_UN_H
41# include <sys/un.h> 42# include <sys/un.h>
42# ifndef UNIX_PATH_MAX 43# ifndef UNIX_PATH_MAX
43 /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ 44/* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */
44# define UNIX_PATH_MAX 104 45# define UNIX_PATH_MAX 104
45# endif /* UNIX_PATH_MAX */ 46# endif /* UNIX_PATH_MAX */
46#endif /* HAVE_SYS_UN_H */ 47#endif /* HAVE_SYS_UN_H */
47 48
48#ifndef HOST_MAX_BYTES 49#ifndef HOST_MAX_BYTES
49# define HOST_MAX_BYTES 255 50# define HOST_MAX_BYTES 255
50#endif 51#endif
51 52
52/* process_request and wrapper macros */ 53/* process_request and wrapper macros */
53#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ 54#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \
54 process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize) 55 process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize)
55#define process_udp_request(addr, port, sbuf, rbuf, rsize) \ 56#define process_udp_request(addr, port, sbuf, rbuf, rsize) \
56 process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize) 57 process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize)
57int process_tcp_request2 (const char *address, int port, 58mp_state_enum process_tcp_request2(const char *server_address, int server_port,
58 const char *sbuffer, char *rbuffer, int rsize); 59 const char *send_buffer, char *recv_buffer, int recv_size);
59int process_request (const char *address, int port, int proto, 60mp_state_enum process_request(const char *server_address, int server_port, int proto,
60 const char *sbuffer, char *rbuffer, int rsize); 61 const char *send_buffer, char *recv_buffer, int recv_size);
61 62
62/* my_connect and wrapper macros */ 63/* my_connect and wrapper macros */
63#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP) 64#define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP)
64#define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP) 65#define my_udp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_UDP)
65int np_net_connect(const char *address, int port, int *sd, int proto); 66mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor, int proto);
66 67
67/* send_request and wrapper macros */ 68/* send_request and wrapper macros */
68#define send_tcp_request(s, sbuf, rbuf, rsize) \ 69#define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize)
69 send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) 70#define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize)
70#define send_udp_request(s, sbuf, rbuf, rsize) \ 71mp_state_enum send_request(int socket, int proto, const char *send_buffer, char *recv_buffer,
71 send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize) 72 int recv_size);
72int send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size);
73
74 73
75/* "is_*" wrapper macros and functions */ 74/* "is_*" wrapper macros and functions */
76bool is_host (const char *); 75bool is_host(const char *);
77bool is_addr (const char *); 76bool is_addr(const char *);
78int dns_lookup (const char *, struct sockaddr_storage *, int); 77bool dns_lookup(const char *, struct sockaddr_storage *, int);
79void host_or_die(const char *str); 78void host_or_die(const char *str);
80#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) 79#define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family)
81#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) 80#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET)
82#ifdef USE_IPV6 81#ifdef USE_IPV6
83# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) 82# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6)
84# define is_hostname(addr) resolve_host_or_addr(addr, address_family) 83# define is_hostname(addr) resolve_host_or_addr(addr, address_family)
85#else 84#else
86# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) 85# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET)
87#endif 86#endif
88 87
89extern unsigned int socket_timeout; 88extern unsigned int socket_timeout;
90extern unsigned int socket_timeout_state; 89extern mp_state_enum socket_timeout_state;
91extern int econn_refuse_state; 90extern mp_state_enum econn_refuse_state;
92extern bool was_refused; 91extern bool was_refused;
93extern int address_family; 92extern int address_family;
94 93
95void socket_timeout_alarm_handler (int) __attribute__((noreturn)); 94void socket_timeout_alarm_handler(int) __attribute__((noreturn));
96 95
97/* SSL-Related functionality */ 96/* SSL-Related functionality */
98#ifdef HAVE_SSL 97#ifdef HAVE_SSL
99# define MP_SSLv2 1 98# define MP_SSLv2 1
100# define MP_SSLv3 2 99# define MP_SSLv3 2
101# define MP_TLSv1 3 100# define MP_TLSv1 3
102# define MP_TLSv1_1 4 101# define MP_TLSv1_1 4
103# define MP_TLSv1_2 5 102# define MP_TLSv1_2 5
104# define MP_SSLv2_OR_NEWER 6 103# define MP_SSLv2_OR_NEWER 6
105# define MP_SSLv3_OR_NEWER 7 104# define MP_SSLv3_OR_NEWER 7
106# define MP_TLSv1_OR_NEWER 8 105# define MP_TLSv1_OR_NEWER 8
107# define MP_TLSv1_1_OR_NEWER 9 106# define MP_TLSv1_1_OR_NEWER 9
108# define MP_TLSv1_2_OR_NEWER 10 107# define MP_TLSv1_2_OR_NEWER 10
109/* maybe this could be merged with the above np_net_connect, via some flags */ 108/* maybe this could be merged with the above np_net_connect, via some flags */
110int np_net_ssl_init(int sd); 109int np_net_ssl_init(int socket);
111int np_net_ssl_init_with_hostname(int sd, char *host_name); 110int np_net_ssl_init_with_hostname(int socket, char *host_name);
112int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version); 111int np_net_ssl_init_with_hostname_and_version(int socket, char *host_name, int version);
113int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey); 112int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name, int version,
114void np_net_ssl_cleanup(); 113 char *cert, char *privkey);
114void np_net_ssl_cleanup(void);
115int np_net_ssl_write(const void *buf, int num); 115int np_net_ssl_write(const void *buf, int num);
116int np_net_ssl_read(void *buf, int num); 116int np_net_ssl_read(void *buf, int num);
117int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 117mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118#endif /* HAVE_SSL */ 119#endif /* HAVE_SSL */
119
120#endif /* _NETUTILS_H_ */ 120#endif /* _NETUTILS_H_ */
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c
index 2ae92d66..e87388b0 100644
--- a/plugins/picohttpparser/picohttpparser.c
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -50,59 +50,61 @@
50# define ALIGNED(n) __attribute__((aligned(n))) 50# define ALIGNED(n) __attribute__((aligned(n)))
51#endif 51#endif
52 52
53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) 53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u)
54 54
55#define CHECK_EOF() \ 55#define CHECK_EOF() \
56 if (buf == buf_end) { \ 56 if (buf == buf_end) { \
57 *ret = -2; \ 57 *ret = -2; \
58 return NULL; \ 58 return NULL; \
59 } 59 }
60 60
61#define EXPECT_CHAR_NO_CHECK(ch) \ 61#define EXPECT_CHAR_NO_CHECK(ch) \
62 if (*buf++ != ch) { \ 62 if (*buf++ != ch) { \
63 *ret = -1; \ 63 *ret = -1; \
64 return NULL; \ 64 return NULL; \
65 } 65 }
66 66
67#define EXPECT_CHAR(ch) \ 67#define EXPECT_CHAR(ch) \
68 CHECK_EOF(); \ 68 CHECK_EOF(); \
69 EXPECT_CHAR_NO_CHECK(ch); 69 EXPECT_CHAR_NO_CHECK(ch);
70 70
71#define ADVANCE_TOKEN(tok, toklen) \ 71#define ADVANCE_TOKEN(tok, toklen) \
72 do { \ 72 do { \
73 const char *tok_start = buf; \ 73 const char *tok_start = buf; \
74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ 74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75 int found2; \ 75 int found2; \
76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ 76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77 if (!found2) { \ 77 if (!found2) { \
78 CHECK_EOF(); \ 78 CHECK_EOF(); \
79 } \ 79 } \
80 while (1) { \ 80 while (1) { \
81 if (*buf == ' ') { \ 81 if (*buf == ' ') { \
82 break; \ 82 break; \
83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ 83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \ 84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85 *ret = -1; \ 85 *ret = -1; \
86 return NULL; \ 86 return NULL; \
87 } \ 87 } \
88 } \ 88 } \
89 ++buf; \ 89 ++buf; \
90 CHECK_EOF(); \ 90 CHECK_EOF(); \
91 } \ 91 } \
92 tok = tok_start; \ 92 tok = tok_start; \
93 toklen = buf - tok_start; \ 93 toklen = buf - tok_start; \
94 } while (0) 94 } while (0)
95 95
96static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 96static const char *token_char_map =
97 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" 97 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
98 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" 98 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
99 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" 99 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
100 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 100 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
104 104 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
105static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) { 105
106static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges,
107 size_t ranges_size, int *found) {
106 *found = 0; 108 *found = 0;
107#if __SSE4_2__ 109#if __SSE4_2__
108 if (likely(buf_end - buf >= 16)) { 110 if (likely(buf_end - buf >= 16)) {
@@ -111,7 +113,8 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const cha
111 size_t left = (buf_end - buf) & ~15; 113 size_t left = (buf_end - buf) & ~15;
112 do { 114 do {
113 __m128i b16 = _mm_loadu_si128((const __m128i *)buf); 115 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
114 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); 116 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
117 _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
115 if (unlikely(r != 16)) { 118 if (unlikely(r != 16)) {
116 buf += r; 119 buf += r;
117 *found = 1; 120 *found = 1;
@@ -130,25 +133,29 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const cha
130 return buf; 133 return buf;
131} 134}
132 135
133static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) { 136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token,
137 size_t *token_len, int *ret) {
134 const char *token_start = buf; 138 const char *token_start = buf;
135 139
136#ifdef __SSE4_2__ 140#ifdef __SSE4_2__
137 static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ 141 static const char ALIGNED(16) ranges1[16] =
138 "\012\037" /* allow SP and up to but not including DEL */ 142 "\0\010" /* allow HT */
139 "\177\177"; /* allow chars w. MSB set */ 143 "\012\037" /* allow SP and up to but not including DEL */
144 "\177\177"; /* allow chars w. MSB set */
140 int found; 145 int found;
141 buf = findchar_fast(buf, buf_end, ranges1, 6, &found); 146 buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
142 if (found) 147 if (found) {
143 goto FOUND_CTL; 148 goto FOUND_CTL;
149 }
144#else 150#else
145 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ 151 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined
152 */
146 while (likely(buf_end - buf >= 8)) { 153 while (likely(buf_end - buf >= 8)) {
147# define DOIT() \ 154# define DOIT() \
148 do { \ 155 do { \
149 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ 156 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
150 goto NonPrintable; \ 157 goto NonPrintable; \
151 ++buf; \ 158 ++buf; \
152 } while (0) 159 } while (0)
153 DOIT(); 160 DOIT();
154 DOIT(); 161 DOIT();
@@ -161,7 +168,8 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const
161# undef DOIT 168# undef DOIT
162 continue; 169 continue;
163 NonPrintable: 170 NonPrintable:
164 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 171 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
172 unlikely(*buf == '\177')) {
165 goto FOUND_CTL; 173 goto FOUND_CTL;
166 } 174 }
167 ++buf; 175 ++buf;
@@ -170,7 +178,8 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const
170 for (;; ++buf) { 178 for (;; ++buf) {
171 CHECK_EOF(); 179 CHECK_EOF();
172 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { 180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
173 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
182 unlikely(*buf == '\177')) {
174 goto FOUND_CTL; 183 goto FOUND_CTL;
175 } 184 }
176 } 185 }
@@ -219,27 +228,28 @@ static const char *is_complete(const char *buf, const char *buf_end, size_t last
219 return NULL; 228 return NULL;
220} 229}
221 230
222#define PARSE_INT(valp_, mul_) \ 231#define PARSE_INT(valp_, mul_) \
223 if (*buf < '0' || '9' < *buf) { \ 232 if (*buf < '0' || '9' < *buf) { \
224 buf++; \ 233 buf++; \
225 *ret = -1; \ 234 *ret = -1; \
226 return NULL; \ 235 return NULL; \
227 } \ 236 } \
228 *(valp_) = (mul_) * (*buf++ - '0'); 237 *(valp_) = (mul_) * (*buf++ - '0');
229 238
230#define PARSE_INT_3(valp_) \ 239#define PARSE_INT_3(valp_) \
231 do { \ 240 do { \
232 int res_ = 0; \ 241 int res_ = 0; \
233 PARSE_INT(&res_, 100) \ 242 PARSE_INT(&res_, 100) \
234 *valp_ = res_; \ 243 *valp_ = res_; \
235 PARSE_INT(&res_, 10) \ 244 PARSE_INT(&res_, 10) \
236 *valp_ += res_; \ 245 *valp_ += res_; \
237 PARSE_INT(&res_, 1) \ 246 PARSE_INT(&res_, 1) \
238 *valp_ += res_; \ 247 *valp_ += res_; \
239 } while (0) 248 } while (0)
240 249
241/* returned pointer is always within [buf, buf_end), or null */ 250/* returned pointer is always within [buf, buf_end), or null */
242static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *ret) { 251static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version,
252 int *minor_version, int *ret) {
243 /* we want at least [HTTP/1.<two chars>] to try to parse */ 253 /* we want at least [HTTP/1.<two chars>] to try to parse */
244 if (buf_end - buf < 9) { 254 if (buf_end - buf < 9) {
245 *ret = -2; 255 *ret = -2;
@@ -260,8 +270,8 @@ static const char *parse_http_version(const char *buf, const char *buf_end, int
260 return buf; 270 return buf;
261} 271}
262 272
263static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, size_t max_headers, 273static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
264 int *ret) { 274 size_t *num_headers, size_t max_headers, int *ret) {
265 for (;; ++*num_headers) { 275 for (;; ++*num_headers) {
266 CHECK_EOF(); 276 CHECK_EOF();
267 if (*buf == '\015') { 277 if (*buf == '\015') {
@@ -337,9 +347,10 @@ static const char *parse_headers(const char *buf, const char *buf_end, struct ph
337 return buf; 347 return buf;
338} 348}
339 349
340static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, 350static const char *parse_request(const char *buf, const char *buf_end, const char **method,
341 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, 351 size_t *method_len, const char **path, size_t *path_len,
342 size_t max_headers, int *ret) { 352 int *major_version, int *minor_version, struct phr_header *headers,
353 size_t *num_headers, size_t max_headers, int *ret) {
343 /* skip first empty line (some clients add CRLF after POST content) */ 354 /* skip first empty line (some clients add CRLF after POST content) */
344 CHECK_EOF(); 355 CHECK_EOF();
345 if (*buf == '\015') { 356 if (*buf == '\015') {
@@ -378,8 +389,9 @@ static const char *parse_request(const char *buf, const char *buf_end, const cha
378 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 389 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
379} 390}
380 391
381int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 392int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len,
382 int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) { 393 const char **path, size_t *path_len, int *major_version, int *minor_version,
394 struct phr_header *headers, size_t *num_headers, size_t last_len) {
383 const char *buf = buf_start, *buf_end = buf_start + len; 395 const char *buf = buf_start, *buf_end = buf_start + len;
384 size_t max_headers = *num_headers; 396 size_t max_headers = *num_headers;
385 int r; 397 int r;
@@ -398,17 +410,18 @@ int phr_parse_request(const char *buf_start, size_t len, const char **method, si
398 return r; 410 return r;
399 } 411 }
400 412
401 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, minor_version, headers, num_headers, 413 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version,
402 max_headers, &r)) == NULL) { 414 minor_version, headers, num_headers, max_headers, &r)) == NULL) {
403 return r; 415 return r;
404 } 416 }
405 417
406 return (int)(buf - buf_start); 418 return (int)(buf - buf_start);
407} 419}
408 420
409static const char *parse_response(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *status, 421static const char *parse_response(const char *buf, const char *buf_end, int *major_version,
410 const char **msg, size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, 422 int *minor_version, int *status, const char **msg,
411 int *ret) { 423 size_t *msg_len, struct phr_header *headers, size_t *num_headers,
424 size_t max_headers, int *ret) {
412 /* parse "HTTP/1.x" */ 425 /* parse "HTTP/1.x" */
413 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 426 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
414 return NULL; 427 return NULL;
@@ -421,7 +434,8 @@ static const char *parse_response(const char *buf, const char *buf_end, int *maj
421 do { 434 do {
422 ++buf; 435 ++buf;
423 } while (*buf == ' '); 436 } while (*buf == ' ');
424 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */ 437 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse
438 */
425 if (buf_end - buf < 4) { 439 if (buf_end - buf < 4) {
426 *ret = -2; 440 *ret = -2;
427 return NULL; 441 return NULL;
@@ -449,8 +463,9 @@ static const char *parse_response(const char *buf, const char *buf_end, int *maj
449 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 463 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
450} 464}
451 465
452int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, int *status, const char **msg, 466int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version,
453 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t last_len) { 467 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
468 size_t *num_headers, size_t last_len) {
454 const char *buf = buf_start, *buf_end = buf + len; 469 const char *buf = buf_start, *buf_end = buf + len;
455 size_t max_headers = *num_headers; 470 size_t max_headers = *num_headers;
456 int r; 471 int r;
@@ -468,15 +483,16 @@ int phr_parse_response(const char *buf_start, size_t len, int *major_version, in
468 return r; 483 return r;
469 } 484 }
470 485
471 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == 486 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len,
472 NULL) { 487 headers, num_headers, max_headers, &r)) == NULL) {
473 return r; 488 return r;
474 } 489 }
475 490
476 return (int)(buf - buf_start); 491 return (int)(buf - buf_start);
477} 492}
478 493
479int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) { 494int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers,
495 size_t *num_headers, size_t last_len) {
480 const char *buf = buf_start, *buf_end = buf + len; 496 const char *buf = buf_start, *buf_end = buf + len;
481 size_t max_headers = *num_headers; 497 size_t max_headers = *num_headers;
482 int r; 498 int r;
@@ -526,8 +542,9 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
526 case CHUNKED_IN_CHUNK_SIZE: 542 case CHUNKED_IN_CHUNK_SIZE:
527 for (;; ++src) { 543 for (;; ++src) {
528 int v; 544 int v;
529 if (src == bufsz) 545 if (src == bufsz) {
530 goto Exit; 546 goto Exit;
547 }
531 if ((v = decode_hex(buf[src])) == -1) { 548 if ((v = decode_hex(buf[src])) == -1) {
532 if (decoder->_hex_count == 0) { 549 if (decoder->_hex_count == 0) {
533 ret = -1; 550 ret = -1;
@@ -548,10 +565,12 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
548 case CHUNKED_IN_CHUNK_EXT: 565 case CHUNKED_IN_CHUNK_EXT:
549 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ 566 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
550 for (;; ++src) { 567 for (;; ++src) {
551 if (src == bufsz) 568 if (src == bufsz) {
552 goto Exit; 569 goto Exit;
553 if (buf[src] == '\012') 570 }
571 if (buf[src] == '\012') {
554 break; 572 break;
573 }
555 } 574 }
556 ++src; 575 ++src;
557 if (decoder->bytes_left_in_chunk == 0) { 576 if (decoder->bytes_left_in_chunk == 0) {
@@ -567,15 +586,17 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
567 case CHUNKED_IN_CHUNK_DATA: { 586 case CHUNKED_IN_CHUNK_DATA: {
568 size_t avail = bufsz - src; 587 size_t avail = bufsz - src;
569 if (avail < decoder->bytes_left_in_chunk) { 588 if (avail < decoder->bytes_left_in_chunk) {
570 if (dst != src) 589 if (dst != src) {
571 memmove(buf + dst, buf + src, avail); 590 memmove(buf + dst, buf + src, avail);
591 }
572 src += avail; 592 src += avail;
573 dst += avail; 593 dst += avail;
574 decoder->bytes_left_in_chunk -= avail; 594 decoder->bytes_left_in_chunk -= avail;
575 goto Exit; 595 goto Exit;
576 } 596 }
577 if (dst != src) 597 if (dst != src) {
578 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); 598 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
599 }
579 src += decoder->bytes_left_in_chunk; 600 src += decoder->bytes_left_in_chunk;
580 dst += decoder->bytes_left_in_chunk; 601 dst += decoder->bytes_left_in_chunk;
581 decoder->bytes_left_in_chunk = 0; 602 decoder->bytes_left_in_chunk = 0;
@@ -584,10 +605,12 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
584 /* fallthru */ 605 /* fallthru */
585 case CHUNKED_IN_CHUNK_CRLF: 606 case CHUNKED_IN_CHUNK_CRLF:
586 for (;; ++src) { 607 for (;; ++src) {
587 if (src == bufsz) 608 if (src == bufsz) {
588 goto Exit; 609 goto Exit;
589 if (buf[src] != '\015') 610 }
611 if (buf[src] != '\015') {
590 break; 612 break;
613 }
591 } 614 }
592 if (buf[src] != '\012') { 615 if (buf[src] != '\012') {
593 ret = -1; 616 ret = -1;
@@ -598,21 +621,26 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
598 break; 621 break;
599 case CHUNKED_IN_TRAILERS_LINE_HEAD: 622 case CHUNKED_IN_TRAILERS_LINE_HEAD:
600 for (;; ++src) { 623 for (;; ++src) {
601 if (src == bufsz) 624 if (src == bufsz) {
602 goto Exit; 625 goto Exit;
603 if (buf[src] != '\015') 626 }
627 if (buf[src] != '\015') {
604 break; 628 break;
629 }
605 } 630 }
606 if (buf[src++] == '\012') 631 if (buf[src++] == '\012') {
607 goto Complete; 632 goto Complete;
633 }
608 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; 634 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
609 /* fallthru */ 635 /* fallthru */
610 case CHUNKED_IN_TRAILERS_LINE_MIDDLE: 636 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
611 for (;; ++src) { 637 for (;; ++src) {
612 if (src == bufsz) 638 if (src == bufsz) {
613 goto Exit; 639 goto Exit;
614 if (buf[src] == '\012') 640 }
641 if (buf[src] == '\012') {
615 break; 642 break;
643 }
616 } 644 }
617 ++src; 645 ++src;
618 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 646 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
@@ -625,13 +653,16 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
625Complete: 653Complete:
626 ret = bufsz - src; 654 ret = bufsz - src;
627Exit: 655Exit:
628 if (dst != src) 656 if (dst != src) {
629 memmove(buf + dst, buf + src, bufsz - src); 657 memmove(buf + dst, buf + src, bufsz - src);
658 }
630 *_bufsz = dst; 659 *_bufsz = dst;
631 return ret; 660 return ret;
632} 661}
633 662
634int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) { return decoder->_state == CHUNKED_IN_CHUNK_DATA; } 663int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
664 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
665}
635 666
636#undef CHECK_EOF 667#undef CHECK_EOF
637#undef EXPECT_CHAR 668#undef EXPECT_CHAR
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h
index 8f13b36f..054c2812 100644
--- a/plugins/picohttpparser/picohttpparser.h
+++ b/plugins/picohttpparser/picohttpparser.h
@@ -30,7 +30,7 @@
30#include <sys/types.h> 30#include <sys/types.h>
31 31
32#ifdef _MSC_VER 32#ifdef _MSC_VER
33#define ssize_t intptr_t 33# define ssize_t intptr_t
34#endif 34#endif
35 35
36#ifdef __cplusplus 36#ifdef __cplusplus
@@ -40,30 +40,33 @@ extern "C" {
40/* contains name and value of a header (name == NULL if is a continuing line 40/* contains name and value of a header (name == NULL if is a continuing line
41 * of a multiline header */ 41 * of a multiline header */
42struct phr_header { 42struct phr_header {
43 const char *name; 43 const char *name;
44 size_t name_len; 44 size_t name_len;
45 const char *value; 45 const char *value;
46 size_t value_len; 46 size_t value_len;
47}; 47};
48 48
49/* returns number of bytes consumed if successful, -2 if request is partial, 49/* returns number of bytes consumed if successful, -2 if request is partial,
50 * -1 if failed */ 50 * -1 if failed */
51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len,
52 int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 52 const char **path, size_t *path_len, int *major_version, int *minor_version,
53 struct phr_header *headers, size_t *num_headers, size_t last_len);
53 54
54/* ditto */ 55/* ditto */
55int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 56int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version,
56 struct phr_header *headers, size_t *num_headers, size_t last_len); 57 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
58 size_t *num_headers, size_t last_len);
57 59
58/* ditto */ 60/* ditto */
59int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers,
62 size_t last_len);
60 63
61/* should be zero-filled before start */ 64/* should be zero-filled before start */
62struct phr_chunked_decoder { 65struct phr_chunked_decoder {
63 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 66 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
64 char consume_trailer; /* if trailing headers should be consumed */ 67 char consume_trailer; /* if trailing headers should be consumed */
65 char _hex_count; 68 char _hex_count;
66 char _state; 69 char _state;
67}; 70};
68 71
69/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 72/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
diff --git a/plugins/popen.c b/plugins/popen.c
index 2b9824bc..c596d1e0 100644
--- a/plugins/popen.c
+++ b/plugins/popen.c
@@ -40,7 +40,6 @@
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;
@@ -69,7 +68,7 @@ void popen_timeout_alarm_handler(int /*signo*/);
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 */
@@ -97,24 +96,28 @@ FILE *spopen(const char *cmdstring) {
97 env[1] = NULL; 96 env[1] = NULL;
98 97
99 /* if no command was passed, return with no error */ 98 /* if no command was passed, return with no error */
100 if (cmdstring == NULL) 99 if (cmdstring == NULL) {
101 return (NULL); 100 return (NULL);
101 }
102 102
103 char *cmd = NULL; 103 char *cmd = NULL;
104 /* 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 */
105 /* (the calling program may want to access it later) */ 105 /* (the calling program may want to access it later) */
106 cmd = malloc(strlen(cmdstring) + 1); 106 cmd = malloc(strlen(cmdstring) + 1);
107 if (cmd == NULL) 107 if (cmd == NULL) {
108 return NULL; 108 return NULL;
109 }
109 strcpy(cmd, cmdstring); 110 strcpy(cmd, cmdstring);
110 111
111 /* This is not a shell, so we don't handle "???" */ 112 /* This is not a shell, so we don't handle "???" */
112 if (strstr(cmdstring, "\"")) 113 if (strstr(cmdstring, "\"")) {
113 return NULL; 114 return NULL;
115 }
114 116
115 /* 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 */
116 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) 118 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
117 return NULL; 119 return NULL;
120 }
118 121
119 int argc; 122 int argc;
120 char **argv = NULL; 123 char **argv = NULL;
@@ -141,15 +144,17 @@ FILE *spopen(const char *cmdstring) {
141 144
142 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ 145 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
143 str++; 146 str++;
144 if (!strstr(str, "'")) 147 if (!strstr(str, "'")) {
145 return NULL; /* balanced? */ 148 return NULL; /* balanced? */
149 }
146 cmd = 1 + strstr(str, "'"); 150 cmd = 1 + strstr(str, "'");
147 str[strcspn(str, "'")] = 0; 151 str[strcspn(str, "'")] = 0;
148 } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) { 152 } else if (strcspn(str, "'") < strcspn(str, " \t\r\n")) {
149 /* handle --option='foo bar' strings */ 153 /* handle --option='foo bar' strings */
150 char *tmp = str + strcspn(str, "'") + 1; 154 char *tmp = str + strcspn(str, "'") + 1;
151 if (!strstr(tmp, "'")) 155 if (!strstr(tmp, "'")) {
152 return NULL; /* balanced? */ 156 return NULL; /* balanced? */
157 }
153 tmp += strcspn(tmp, "'") + 1; 158 tmp += strcspn(tmp, "'") + 1;
154 *tmp = 0; 159 *tmp = 0;
155 cmd = tmp + 1; 160 cmd = tmp + 1;
@@ -162,8 +167,9 @@ FILE *spopen(const char *cmdstring) {
162 } 167 }
163 } 168 }
164 169
165 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) 170 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
166 cmd = NULL; 171 cmd = NULL;
172 }
167 173
168 argv[i++] = str; 174 argv[i++] = str;
169 } 175 }
@@ -172,22 +178,26 @@ FILE *spopen(const char *cmdstring) {
172 long maxfd = mp_open_max(); 178 long maxfd = mp_open_max();
173 179
174 if (childpid == NULL) { /* first time through */ 180 if (childpid == NULL) { /* first time through */
175 if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) 181 if ((childpid = calloc((size_t)maxfd, sizeof(pid_t))) == NULL) {
176 return (NULL); 182 return (NULL);
183 }
177 } 184 }
178 185
179 if (child_stderr_array == NULL) { /* first time through */ 186 if (child_stderr_array == NULL) { /* first time through */
180 if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) 187 if ((child_stderr_array = calloc((size_t)maxfd, sizeof(int))) == NULL) {
181 return (NULL); 188 return (NULL);
189 }
182 } 190 }
183 191
184 int pfd[2]; 192 int pfd[2];
185 if (pipe(pfd) < 0) 193 if (pipe(pfd) < 0) {
186 return (NULL); /* errno set by pipe() */ 194 return (NULL); /* errno set by pipe() */
195 }
187 196
188 int pfderr[2]; 197 int pfderr[2];
189 if (pipe(pfderr) < 0) 198 if (pipe(pfderr) < 0) {
190 return (NULL); /* errno set by pipe() */ 199 return (NULL); /* errno set by pipe() */
200 }
191 201
192#ifdef REDHAT_SPOPEN_ERROR 202#ifdef REDHAT_SPOPEN_ERROR
193 if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) { 203 if (signal(SIGCHLD, popen_sigchld_handler) == SIG_ERR) {
@@ -196,8 +206,9 @@ FILE *spopen(const char *cmdstring) {
196#endif 206#endif
197 207
198 pid_t pid; 208 pid_t pid;
199 if ((pid = fork()) < 0) 209 if ((pid = fork()) < 0) {
200 return (NULL); /* errno set by fork() */ 210 return (NULL); /* errno set by fork() */
211 }
201 212
202 if (pid == 0) { /* child */ 213 if (pid == 0) { /* child */
203 close(pfd[0]); 214 close(pfd[0]);
@@ -211,17 +222,20 @@ FILE *spopen(const char *cmdstring) {
211 close(pfderr[1]); 222 close(pfderr[1]);
212 } 223 }
213 /* close all descriptors in childpid[] */ 224 /* close all descriptors in childpid[] */
214 for (i = 0; i < maxfd; i++) 225 for (i = 0; i < maxfd; i++) {
215 if (childpid[i] > 0) 226 if (childpid[i] > 0) {
216 close(i); 227 close(i);
228 }
229 }
217 230
218 execve(argv[0], argv, env); 231 execve(argv[0], argv, env);
219 _exit(0); 232 _exit(0);
220 } 233 }
221 234
222 close(pfd[1]); /* parent */ 235 close(pfd[1]); /* parent */
223 if ((child_process = fdopen(pfd[0], "r")) == NULL) 236 if ((child_process = fdopen(pfd[0], "r")) == NULL) {
224 return (NULL); 237 return (NULL);
238 }
225 close(pfderr[1]); 239 close(pfderr[1]);
226 240
227 childpid[fileno(child_process)] = pid; /* remember child pid for this fd */ 241 childpid[fileno(child_process)] = pid; /* remember child pid for this fd */
@@ -230,17 +244,20 @@ FILE *spopen(const char *cmdstring) {
230} 244}
231 245
232int spclose(FILE *fp) { 246int spclose(FILE *fp) {
233 if (childpid == NULL) 247 if (childpid == NULL) {
234 return (1); /* popen() has never been called */ 248 return (1); /* popen() has never been called */
249 }
235 250
236 pid_t pid; 251 pid_t pid;
237 int fd = fileno(fp); 252 int fd = fileno(fp);
238 if ((pid = childpid[fd]) == 0) 253 if ((pid = childpid[fd]) == 0) {
239 return (1); /* fp wasn't opened by popen() */ 254 return (1); /* fp wasn't opened by popen() */
255 }
240 256
241 childpid[fd] = 0; 257 childpid[fd] = 0;
242 if (fclose(fp) == EOF) 258 if (fclose(fp) == EOF) {
243 return (1); 259 return (1);
260 }
244 261
245#ifdef REDHAT_SPOPEN_ERROR 262#ifdef REDHAT_SPOPEN_ERROR
246 while (!childtermd) 263 while (!childtermd)
@@ -248,20 +265,24 @@ int spclose(FILE *fp) {
248#endif 265#endif
249 266
250 int status; 267 int status;
251 while (waitpid(pid, &status, 0) < 0) 268 while (waitpid(pid, &status, 0) < 0) {
252 if (errno != EINTR) 269 if (errno != EINTR) {
253 return (1); /* error other than EINTR from waitpid() */ 270 return (1); /* error other than EINTR from waitpid() */
271 }
272 }
254 273
255 if (WIFEXITED(status)) 274 if (WIFEXITED(status)) {
256 return (WEXITSTATUS(status)); /* return child's termination status */ 275 return (WEXITSTATUS(status)); /* return child's termination status */
276 }
257 277
258 return (1); 278 return (1);
259} 279}
260 280
261#ifdef REDHAT_SPOPEN_ERROR 281#ifdef REDHAT_SPOPEN_ERROR
262void popen_sigchld_handler(int signo) { 282void popen_sigchld_handler(int signo) {
263 if (signo == SIGCHLD) 283 if (signo == SIGCHLD) {
264 childtermd = 1; 284 childtermd = 1;
285 }
265} 286}
266#endif 287#endif
267 288
diff --git a/plugins/popen.h b/plugins/popen.h
index 1ea69632..e318ce25 100644
--- a/plugins/popen.h
+++ b/plugins/popen.h
@@ -1,13 +1,13 @@
1/****************************************************************************** 1/******************************************************************************
2* 2 *
3* 3 *
4*****************************************************************************/ 4 *****************************************************************************/
5 5
6FILE *spopen (const char *); 6FILE *spopen(const char *);
7int spclose (FILE *); 7int spclose(FILE *);
8void popen_timeout_alarm_handler (int); 8void popen_timeout_alarm_handler(int);
9 9
10pid_t *childpid=NULL; 10pid_t *childpid = NULL;
11int *child_stderr_array=NULL; 11int *child_stderr_array = NULL;
12FILE *child_process=NULL; 12FILE *child_process = NULL;
13FILE *child_stderr=NULL; 13FILE *child_stderr = NULL;
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index 74843149..7c583b85 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -40,6 +40,7 @@
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
@@ -52,7 +53,7 @@
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 */
@@ -86,8 +87,9 @@ extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(
86 * through this api and thus achieve async-safeness throughout the api */ 87 * through this api and thus achieve async-safeness throughout the api */
87void np_runcmd_init(void) { 88void np_runcmd_init(void) {
88 long maxfd = mp_open_max(); 89 long maxfd = mp_open_max();
89 if (!np_pids) 90 if (!np_pids) {
90 np_pids = calloc(maxfd, sizeof(pid_t)); 91 np_pids = calloc(maxfd, sizeof(pid_t));
92 }
91} 93}
92 94
93/* Start running a command */ 95/* Start running a command */
@@ -105,8 +107,9 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
105 107
106 int i = 0; 108 int i = 0;
107 109
108 if (!np_pids) 110 if (!np_pids) {
109 NP_RUNCMD_INIT; 111 NP_RUNCMD_INIT;
112 }
110 113
111 env[0] = strdup("LC_ALL=C"); 114 env[0] = strdup("LC_ALL=C");
112 env[1] = NULL; 115 env[1] = NULL;
@@ -114,18 +117,21 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
114 /* 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 */
115 /* (the calling program may want to access it later) */ 118 /* (the calling program may want to access it later) */
116 cmdlen = strlen(cmdstring); 119 cmdlen = strlen(cmdstring);
117 if ((cmd = malloc(cmdlen + 1)) == NULL) 120 if ((cmd = malloc(cmdlen + 1)) == NULL) {
118 return -1; 121 return -1;
122 }
119 memcpy(cmd, cmdstring, cmdlen); 123 memcpy(cmd, cmdstring, cmdlen);
120 cmd[cmdlen] = '\0'; 124 cmd[cmdlen] = '\0';
121 125
122 /* This is not a shell, so we don't handle "???" */ 126 /* This is not a shell, so we don't handle "???" */
123 if (strstr(cmdstring, "\"")) 127 if (strstr(cmdstring, "\"")) {
124 return -1; 128 return -1;
129 }
125 130
126 /* 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 */
127 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) 132 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
128 return -1; 133 return -1;
134 }
129 135
130 /* 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
131 * 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 */
@@ -144,8 +150,9 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
144 150
145 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ 151 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
146 str++; 152 str++;
147 if (!strstr(str, "'")) 153 if (!strstr(str, "'")) {
148 return -1; /* balanced? */ 154 return -1; /* balanced? */
155 }
149 cmd = 1 + strstr(str, "'"); 156 cmd = 1 + strstr(str, "'");
150 str[strcspn(str, "'")] = 0; 157 str[strcspn(str, "'")] = 0;
151 } else { 158 } else {
@@ -157,14 +164,16 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
157 } 164 }
158 } 165 }
159 166
160 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) 167 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
161 cmd = NULL; 168 cmd = NULL;
169 }
162 170
163 argv[i++] = str; 171 argv[i++] = str;
164 } 172 }
165 173
166 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) 174 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) {
167 return -1; /* errno set by the failing function */ 175 return -1; /* errno set by the failing function */
176 }
168 177
169 /* child runs exceve() and _exit. */ 178 /* child runs exceve() and _exit. */
170 if (pid == 0) { 179 if (pid == 0) {
@@ -189,9 +198,11 @@ static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
189 * This is executed in a separate address space (pure child), 198 * This is executed in a separate address space (pure child),
190 * so we don't have to worry about async safety */ 199 * so we don't have to worry about async safety */
191 long maxfd = mp_open_max(); 200 long maxfd = mp_open_max();
192 for (i = 0; i < maxfd; i++) 201 for (i = 0; i < maxfd; i++) {
193 if (np_pids[i] > 0) 202 if (np_pids[i] > 0) {
194 close(i); 203 close(i);
204 }
205 }
195 206
196 execve(argv[0], argv, env); 207 execve(argv[0], argv, env);
197 _exit(STATE_UNKNOWN); 208 _exit(STATE_UNKNOWN);
@@ -214,17 +225,21 @@ static int np_runcmd_close(int fd) {
214 225
215 /* make sure this fd was opened by popen() */ 226 /* make sure this fd was opened by popen() */
216 long maxfd = mp_open_max(); 227 long maxfd = mp_open_max();
217 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) {
218 return -1; 229 return -1;
230 }
219 231
220 np_pids[fd] = 0; 232 np_pids[fd] = 0;
221 if (close(fd) == -1) 233 if (close(fd) == -1) {
222 return -1; 234 return -1;
235 }
223 236
224 /* EINTR is ok (sort of), everything else is bad */ 237 /* EINTR is ok (sort of), everything else is bad */
225 while (waitpid(pid, &status, 0) < 0) 238 while (waitpid(pid, &status, 0) < 0) {
226 if (errno != EINTR) 239 if (errno != EINTR) {
227 return -1; 240 return -1;
241 }
242 }
228 243
229 /* return child's termination status */ 244 /* return child's termination status */
230 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 245 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
@@ -232,15 +247,18 @@ static int np_runcmd_close(int fd) {
232 247
233void runcmd_timeout_alarm_handler(int signo) { 248void runcmd_timeout_alarm_handler(int signo) {
234 249
235 if (signo == SIGALRM) 250 if (signo == SIGALRM) {
236 puts(_("CRITICAL - Plugin timed out while executing system call")); 251 puts(_("CRITICAL - Plugin timed out while executing system call"));
252 }
237 253
238 long maxfd = mp_open_max(); 254 long maxfd = mp_open_max();
239 if (np_pids) 255 if (np_pids) {
240 for (long int i = 0; i < maxfd; i++) { 256 for (long int i = 0; i < maxfd; i++) {
241 if (np_pids[i] != 0) 257 if (np_pids[i] != 0) {
242 kill(np_pids[i], SIGKILL); 258 kill(np_pids[i], SIGKILL);
259 }
243 } 260 }
261 }
244 262
245 exit(STATE_CRITICAL); 263 exit(STATE_CRITICAL);
246} 264}
@@ -269,15 +287,17 @@ static int np_fetch_output(int fd, output *op, int flags) {
269 287
270 /* some plugins may want to keep output unbroken, and some commands 288 /* some plugins may want to keep output unbroken, and some commands
271 * will yield no output, so return here for those */ 289 * will yield no output, so return here for those */
272 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) 290 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) {
273 return op->buflen; 291 return op->buflen;
292 }
274 293
275 /* and some may want both */ 294 /* and some may want both */
276 if (flags & RUNCMD_NO_ASSOC) { 295 if (flags & RUNCMD_NO_ASSOC) {
277 buf = malloc(op->buflen); 296 buf = malloc(op->buflen);
278 memcpy(buf, op->buf, op->buflen); 297 memcpy(buf, op->buf, op->buflen);
279 } else 298 } else {
280 buf = op->buf; 299 buf = op->buf;
300 }
281 301
282 op->line = NULL; 302 op->line = NULL;
283 op->lens = NULL; 303 op->lens = NULL;
@@ -298,8 +318,9 @@ static int np_fetch_output(int fd, output *op, int flags) {
298 op->line[lineno] = &buf[i]; 318 op->line[lineno] = &buf[i];
299 319
300 /* hop to next newline or end of buffer */ 320 /* hop to next newline or end of buffer */
301 while (buf[i] != '\n' && i < op->buflen) 321 while (buf[i] != '\n' && i < op->buflen) {
302 i++; 322 i++;
323 }
303 buf[i] = '\0'; 324 buf[i] = '\0';
304 325
305 /* calculate the string length using pointer difference */ 326 /* calculate the string length using pointer difference */
@@ -316,18 +337,23 @@ int np_runcmd(const char *cmd, output *out, output *err, int flags) {
316 int fd, pfd_out[2], pfd_err[2]; 337 int fd, pfd_out[2], pfd_err[2];
317 338
318 /* initialize the structs */ 339 /* initialize the structs */
319 if (out) 340 if (out) {
320 memset(out, 0, sizeof(output)); 341 memset(out, 0, sizeof(output));
321 if (err) 342 }
343 if (err) {
322 memset(err, 0, sizeof(output)); 344 memset(err, 0, sizeof(output));
345 }
323 346
324 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) 347 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) {
325 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 348 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
349 }
326 350
327 if (out) 351 if (out) {
328 out->lines = np_fetch_output(pfd_out[0], out, flags); 352 out->lines = np_fetch_output(pfd_out[0], out, flags);
329 if (err) 353 }
354 if (err) {
330 err->lines = np_fetch_output(pfd_err[0], err, flags); 355 err->lines = np_fetch_output(pfd_err[0], err, flags);
356 }
331 357
332 return np_runcmd_close(fd); 358 return np_runcmd_close(fd);
333} 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 3c928413..0e6d7525 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -26,9 +26,12 @@
26 * 26 *
27 *****************************************************************************/ 27 *****************************************************************************/
28 28
29#include "output.h"
29#define MAX_CN_LENGTH 256 30#define MAX_CN_LENGTH 256
30#include "common.h" 31#include "common.h"
31#include "netutils.h" 32#include "netutils.h"
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;
@@ -36,13 +39,16 @@ static SSL *s = NULL;
36 39
37int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); } 40int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); }
38 41
39int np_net_ssl_init_with_hostname(int sd, char *host_name) { return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); } 42int np_net_ssl_init_with_hostname(int sd, char *host_name) {
43 return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0);
44}
40 45
41int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) { 46int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) {
42 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);
43} 48}
44 49
45int 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) {
46 long options = 0; 52 long options = 0;
47 53
48 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { 54 if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) {
@@ -74,7 +80,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
74# endif 80# endif
75 case MP_TLSv1_1: /* TLSv1.1 protocol */ 81 case MP_TLSv1_1: /* TLSv1.1 protocol */
76# if !defined(SSL_OP_NO_TLSv1_1) 82# if !defined(SSL_OP_NO_TLSv1_1)
77 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."));
78 return STATE_UNKNOWN; 85 return STATE_UNKNOWN;
79# else 86# else
80 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); 87 SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
@@ -83,7 +90,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
83# endif 90# endif
84 case MP_TLSv1_2: /* TLSv1.2 protocol */ 91 case MP_TLSv1_2: /* TLSv1.2 protocol */
85# if !defined(SSL_OP_NO_TLSv1_2) 92# if !defined(SSL_OP_NO_TLSv1_2)
86 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."));
87 return STATE_UNKNOWN; 95 return STATE_UNKNOWN;
88# else 96# else
89 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); 97 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
@@ -144,8 +152,9 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int
144 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 152 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
145 if ((s = SSL_new(ctx)) != NULL) { 153 if ((s = SSL_new(ctx)) != NULL) {
146# ifdef SSL_set_tlsext_host_name 154# ifdef SSL_set_tlsext_host_name
147 if (host_name != NULL) 155 if (host_name != NULL) {
148 SSL_set_tlsext_host_name(s, host_name); 156 SSL_set_tlsext_host_name(s, host_name);
157 }
149# endif 158# endif
150 SSL_set_fd(s, sd); 159 SSL_set_fd(s, sd);
151 if (SSL_connect(s) == 1) { 160 if (SSL_connect(s) == 1) {
@@ -181,63 +190,54 @@ int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num);
181 190
182int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } 191int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); }
183 192
184int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit) { 193mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
194 int days_till_exp_crit) {
185# ifdef USE_OPENSSL 195# ifdef USE_OPENSSL
186 X509_NAME *subj = NULL;
187 char timestamp[50] = "";
188 char cn[MAX_CN_LENGTH] = "";
189 char *tz;
190
191 int cnlen = -1;
192 int status = STATE_UNKNOWN;
193
194 ASN1_STRING *tm;
195 int offset;
196 struct tm stamp;
197 float time_left;
198 int days_left;
199 int time_remaining;
200 time_t tm_t;
201
202 if (!certificate) { 196 if (!certificate) {
203 printf("%s\n", _("CRITICAL - Cannot retrieve server certificate.")); 197 printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
204 return STATE_CRITICAL; 198 return STATE_CRITICAL;
205 } 199 }
206 200
207 /* Extract CN from certificate subject */ 201 /* Extract CN from certificate subject */
208 subj = X509_get_subject_name(certificate); 202 X509_NAME *subj = X509_get_subject_name(certificate);
209 203
210 if (!subj) { 204 if (!subj) {
211 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); 205 printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
212 return STATE_CRITICAL; 206 return STATE_CRITICAL;
213 } 207 }
214 cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); 208
215 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) {
216 strcpy(cn, _("Unknown CN")); 212 strcpy(cn, _("Unknown CN"));
213 }
217 214
218 /* Retrieve timestamp of certificate */ 215 /* Retrieve timestamp of certificate */
219 tm = X509_get_notAfter(certificate); 216 ASN1_STRING *tm = X509_get_notAfter(certificate);
220 217
218 int offset = 0;
219 struct tm stamp = {};
221 /* Generate tm structure to process timestamp */ 220 /* Generate tm structure to process timestamp */
222 if (tm->type == V_ASN1_UTCTIME) { 221 if (tm->type == V_ASN1_UTCTIME) {
223 if (tm->length < 10) { 222 if (tm->length < 10) {
224 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 223 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
225 return STATE_CRITICAL; 224 return STATE_CRITICAL;
226 } else {
227 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
228 if (stamp.tm_year < 50)
229 stamp.tm_year += 100;
230 offset = 0;
231 } 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
232 } else { 232 } else {
233 if (tm->length < 12) { 233 if (tm->length < 12) {
234 printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); 234 printf("%s\n", _("CRITICAL - Wrong time format in certificate."));
235 return STATE_CRITICAL; 235 return STATE_CRITICAL;
236 } else {
237 stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
238 stamp.tm_year -= 1900;
239 offset = 2;
240 } 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;
241 } 241 }
242 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; 242 stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
243 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); 243 stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
@@ -246,48 +246,60 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
246 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0'); 246 stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0');
247 stamp.tm_isdst = -1; 247 stamp.tm_isdst = -1;
248 248
249 tm_t = timegm(&stamp); 249 time_t tm_t = timegm(&stamp);
250 time_left = difftime(tm_t, time(NULL)); 250 float time_left = difftime(tm_t, time(NULL));
251 days_left = time_left / 86400; 251 int days_left = time_left / 86400;
252 tz = getenv("TZ"); 252 char *tz = getenv("TZ");
253 setenv("TZ", "GMT", 1); 253 setenv("TZ", "GMT", 1);
254 tzset(); 254 tzset();
255
256 char timestamp[50] = "";
255 strftime(timestamp, 50, "%c %z", localtime(&tm_t)); 257 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
256 if (tz) 258 if (tz) {
257 setenv("TZ", tz, 1); 259 setenv("TZ", tz, 1);
258 else 260 } else {
259 unsetenv("TZ"); 261 unsetenv("TZ");
262 }
263
260 tzset(); 264 tzset();
261 265
266 int time_remaining;
267 mp_state_enum status = STATE_UNKNOWN;
262 if (days_left > 0 && days_left <= days_till_exp_warn) { 268 if (days_left > 0 && days_left <= days_till_exp_warn) {
263 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, 269 printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"),
264 days_left, timestamp); 270 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp);
265 if (days_left > days_till_exp_crit) 271 if (days_left > days_till_exp_crit) {
266 status = STATE_WARNING; 272 status = STATE_WARNING;
267 else 273 } else {
268 status = STATE_CRITICAL; 274 status = STATE_CRITICAL;
275 }
269 } else if (days_left == 0 && time_left > 0) { 276 } else if (days_left == 0 && time_left > 0) {
270 if (time_left >= 3600) 277 if (time_left >= 3600) {
271 time_remaining = (int)time_left / 3600; 278 time_remaining = (int)time_left / 3600;
272 else 279 } else {
273 time_remaining = (int)time_left / 60; 280 time_remaining = (int)time_left / 60;
281 }
274 282
275 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, 283 printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"),
276 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); 284 (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining,
285 time_left >= 3600 ? "hours" : "minutes", timestamp);
277 286
278 if (days_left > days_till_exp_crit) 287 if (days_left > days_till_exp_crit) {
279 status = STATE_WARNING; 288 status = STATE_WARNING;
280 else 289 } else {
281 status = STATE_CRITICAL; 290 status = STATE_CRITICAL;
291 }
282 } else if (time_left < 0) { 292 } else if (time_left < 0) {
283 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); 293 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp);
284 status = STATE_CRITICAL; 294 status = STATE_CRITICAL;
285 } else if (days_left == 0) { 295 } else if (days_left == 0) {
286 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"),
287 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) {
288 status = STATE_WARNING; 299 status = STATE_WARNING;
289 else 300 } else {
290 status = STATE_CRITICAL; 301 status = STATE_CRITICAL;
302 }
291 } else { 303 } else {
292 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); 304 printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp);
293 status = STATE_OK; 305 status = STATE_OK;
@@ -300,7 +312,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int
300# endif /* USE_OPENSSL */ 312# endif /* USE_OPENSSL */
301} 313}
302 314
303int 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) {
304# ifdef USE_OPENSSL 316# ifdef USE_OPENSSL
305 X509 *certificate = NULL; 317 X509 *certificate = NULL;
306 certificate = SSL_get_peer_certificate(s); 318 certificate = SSL_get_peer_certificate(s);
@@ -311,4 +323,136 @@ int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
311# endif /* USE_OPENSSL */ 323# endif /* USE_OPENSSL */
312} 324}
313 325
326mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn,
327 int days_till_exp_crit) {
328 mp_subcheck sc_cert = mp_subcheck_init();
329# ifdef USE_OPENSSL
330 if (!certificate) {
331 xasprintf(&sc_cert.output, _("No server certificate present to inspect"));
332 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
333 return sc_cert;
334 }
335
336 /* Extract CN from certificate subject */
337 X509_NAME *subj = X509_get_subject_name(certificate);
338
339 if (!subj) {
340 xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject"));
341 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
342 return sc_cert;
343 }
344
345 char commonName[MAX_CN_LENGTH] = "";
346 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName));
347 if (cnlen == -1) {
348 strcpy(commonName, _("Unknown CN"));
349 }
350
351 /* Retrieve timestamp of certificate */
352 ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate);
353
354 int offset = 0;
355 struct tm stamp = {};
356 /* Generate tm structure to process timestamp */
357 if (expiry_timestamp->type == V_ASN1_UTCTIME) {
358 if (expiry_timestamp->length < 10) {
359 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
360 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
361 return sc_cert;
362 }
363
364 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0');
365 if (stamp.tm_year < 50) {
366 stamp.tm_year += 100;
367 }
368
369 offset = 0;
370 } else {
371 if (expiry_timestamp->length < 12) {
372 xasprintf(&sc_cert.output, _("Wrong time format in certificate"));
373 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
374 return sc_cert;
375 }
376 stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 +
377 (expiry_timestamp->data[1] - '0') * 100 +
378 (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0');
379 stamp.tm_year -= 1900;
380 offset = 2;
381 }
382
383 stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 +
384 (expiry_timestamp->data[3 + offset] - '0') - 1;
385 stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 +
386 (expiry_timestamp->data[5 + offset] - '0');
387 stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 +
388 (expiry_timestamp->data[7 + offset] - '0');
389 stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 +
390 (expiry_timestamp->data[9 + offset] - '0');
391 stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 +
392 (expiry_timestamp->data[11 + offset] - '0');
393 stamp.tm_isdst = -1;
394
395 time_t tm_t = timegm(&stamp);
396 double time_left = difftime(tm_t, time(NULL));
397 int days_left = (int)(time_left / 86400);
398 char *timeZone = getenv("TZ");
399 setenv("TZ", "GMT", 1);
400 tzset();
401
402 char timestamp[50] = "";
403 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
404 if (timeZone) {
405 setenv("TZ", timeZone, 1);
406 } else {
407 unsetenv("TZ");
408 }
409
410 tzset();
411
412 int time_remaining;
413 if (days_left > 0 && days_left <= days_till_exp_warn) {
414 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName,
415 days_left, timestamp);
416 if (days_left > days_till_exp_crit) {
417 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
418 } else {
419 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
420 }
421 } else if (days_left == 0 && time_left > 0) {
422 if (time_left >= 3600) {
423 time_remaining = (int)time_left / 3600;
424 } else {
425 time_remaining = (int)time_left / 60;
426 }
427
428 xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName,
429 time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp);
430
431 if (days_left > days_till_exp_crit) {
432 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
433 } else {
434 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
435 }
436 } else if (time_left < 0) {
437 xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp);
438 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
439 } else if (days_left == 0) {
440 xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, timestamp);
441 if (days_left > days_till_exp_crit) {
442 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
443 } else {
444 sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL);
445 }
446 } else {
447 xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, timestamp);
448 sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK);
449 }
450 X509_free(certificate);
451 return sc_cert;
452# else /* ifndef USE_OPENSSL */
453 xasprintf(&sc_cert.output, _("Plugin does not support checking certificates"));
454 sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING);
455 return sc_cert;
456# endif /* USE_OPENSSL */
457}
314#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 eaa81083..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]+MiB out of [0-9]+MiB\)/';
12my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
13my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/';
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/lib/tests/test_disk.c b/plugins/tests/test_check_disk.c
index c18db7a4..32b46c2d 100644
--- a/lib/tests/test_disk.c
+++ b/plugins/tests/test_check_disk.c
@@ -17,28 +17,17 @@
17 *****************************************************************************/ 17 *****************************************************************************/
18 18
19#include "common.h" 19#include "common.h"
20#include "utils_disk.h" 20#include "../check_disk.d/utils_disk.h"
21#include "tap.h" 21#include "../../tap/tap.h"
22#include "regex.h" 22#include "regex.h"
23 23
24void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); 24void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags,
25 int expect, char *desc);
25 26
26int main(int argc, char **argv) { 27int main(int argc, char **argv) {
27 struct name_list *exclude_filesystem = NULL; 28 plan_tests(35);
28 struct name_list *exclude_fstype = NULL;
29 struct name_list *dummy_mountlist = NULL;
30 struct name_list *temp_name;
31 struct parameter_list *paths = NULL;
32 struct parameter_list *p, *prev = NULL, *last = NULL;
33
34 struct mount_entry *dummy_mount_list;
35 struct mount_entry *me;
36 struct mount_entry **mtail = &dummy_mount_list;
37 int cflags = REG_NOSUB | REG_EXTENDED;
38 int found = 0, count = 0;
39
40 plan_tests(33);
41 29
30 struct name_list *exclude_filesystem = NULL;
42 ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); 31 ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list");
43 np_add_name(&exclude_filesystem, "/var/log"); 32 np_add_name(&exclude_filesystem, "/var/log");
44 ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); 33 ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now");
@@ -47,6 +36,7 @@ int main(int argc, char **argv) {
47 ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); 36 ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now");
48 ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); 37 ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list");
49 38
39 struct name_list *exclude_fstype = NULL;
50 ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); 40 ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list");
51 np_add_name(&exclude_fstype, "iso9660"); 41 np_add_name(&exclude_fstype, "iso9660");
52 ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); 42 ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now");
@@ -59,7 +49,9 @@ int main(int argc, char **argv) {
59 } 49 }
60 */ 50 */
61 51
62 me = (struct mount_entry *)malloc(sizeof *me); 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);
63 me->me_devname = strdup("/dev/c0t0d0s0"); 55 me->me_devname = strdup("/dev/c0t0d0s0");
64 me->me_mountdir = strdup("/"); 56 me->me_mountdir = strdup("/");
65 *mtail = me; 57 *mtail = me;
@@ -77,50 +69,70 @@ int main(int argc, char **argv) {
77 *mtail = me; 69 *mtail = me;
78 mtail = &me->me_next; 70 mtail = &me->me_next;
79 71
72 int cflags = REG_NOSUB | REG_EXTENDED;
80 np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); 73 np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a"));
81 np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:")); 74 np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3,
82 np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:")); 75 strdup("regex on dev names:"));
83 np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0, strdup("regi on non existent dev/path:")); 76 np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0,
84 np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3, strdup("partial devname regex match:")); 77 strdup("regex on non existent dev/path:"));
85 np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1, strdup("partial devname regex match:")); 78 np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0,
86 np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1, strdup("partial devname regi match:")); 79 strdup("regi on non existent dev/path:"));
87 np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1, strdup("partial pathname regex match:")); 80 np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3,
88 np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1, strdup("partial pathname regi match:")); 81 strdup("partial devname regex match:"));
89 np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); 82 np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1,
90 np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); 83 strdup("partial devname regex match:"));
91 84 np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1,
92 np_add_parameter(&paths, "/home/groups"); 85 strdup("partial devname regi match:"));
93 np_add_parameter(&paths, "/var"); 86 np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1,
94 np_add_parameter(&paths, "/tmp"); 87 strdup("partial pathname regex match:"));
95 np_add_parameter(&paths, "/home/tonvoon"); 88 np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1,
96 np_add_parameter(&paths, "/dev/c2t0d0s0"); 89 strdup("partial pathname regi match:"));
97 90 np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2,
98 np_set_best_match(paths, dummy_mount_list, false); 91 strdup("grouped regex pathname match:"));
99 for (p = paths; p; p = p->name_next) { 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)) {
100 struct mount_entry *temp_me; 105 struct mount_entry *temp_me;
101 temp_me = p->best_match; 106 temp_me = p->best_match;
102 if (!strcmp(p->name, "/home/groups")) { 107 if (!strcmp(p->name, "/home/groups")) {
103 ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/groups got right best match: /home"); 108 ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"),
109 "/home/groups got right best match: /home");
104 } else if (!strcmp(p->name, "/var")) { 110 } else if (!strcmp(p->name, "/var")) {
105 ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var"); 111 ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var");
106 } else if (!strcmp(p->name, "/tmp")) { 112 } else if (!strcmp(p->name, "/tmp")) {
107 ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /"); 113 ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /");
108 } else if (!strcmp(p->name, "/home/tonvoon")) { 114 } else if (!strcmp(p->name, "/home/tonvoon")) {
109 ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/tonvoon got right best match: /home"); 115 ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"),
116 "/home/tonvoon got right best match: /home");
110 } else if (!strcmp(p->name, "/dev/c2t0d0s0")) { 117 } else if (!strcmp(p->name, "/dev/c2t0d0s0")) {
111 ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"), "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0"); 118 ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"),
119 "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0");
112 } 120 }
113 } 121 }
114 122
115 paths = NULL; /* Bad boy - should free, but this is a test suite */ 123 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
116 np_add_parameter(&paths, "/home/groups"); 124 mp_int_fs_list_del(&test_paths, p);
117 np_add_parameter(&paths, "/var"); 125 }
118 np_add_parameter(&paths, "/tmp"); 126 ok(test_paths.length == 0, "List delete sets counter properly");
119 np_add_parameter(&paths, "/home/tonvoon");
120 np_add_parameter(&paths, "/home");
121 127
122 np_set_best_match(paths, dummy_mount_list, true); 128 mp_int_fs_list_append(&test_paths, "/home/groups");
123 for (p = paths; p; p = p->name_next) { 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)) {
124 if (!strcmp(p->name, "/home/groups")) { 136 if (!strcmp(p->name, "/home/groups")) {
125 ok(!p->best_match, "/home/groups correctly not found"); 137 ok(!p->best_match, "/home/groups correctly not found");
126 } else if (!strcmp(p->name, "/var")) { 138 } else if (!strcmp(p->name, "/var")) {
@@ -134,59 +146,68 @@ int main(int argc, char **argv) {
134 } 146 }
135 } 147 }
136 148
149 bool found = false;
137 /* test deleting first element in paths */ 150 /* test deleting first element in paths */
138 paths = np_del_parameter(paths, NULL); 151 mp_int_fs_list_del(&test_paths, NULL);
139 for (p = paths; p; p = p->name_next) { 152 for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) {
140 if (!strcmp(p->name, "/home/groups")) 153 if (!strcmp(p->name, "/home/groups")) {
141 found = 1; 154 found = true;
155 }
142 } 156 }
143 ok(found == 0, "first element successfully deleted"); 157 ok(!found, "first element successfully deleted");
144 found = 0; 158 found = false;
145 159
146 p = paths; 160 parameter_list_elem *prev = NULL;
147 while (p) { 161 parameter_list_elem *p = NULL;
148 if (!strcmp(p->name, "/tmp")) 162 for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) {
149 p = np_del_parameter(p, prev); 163 if (!strcmp(path->name, "/tmp")) {
150 else { 164 mp_int_fs_list_del(&test_paths, path);
151 prev = p;
152 p = p->name_next;
153 } 165 }
166 p = path;
154 } 167 }
155 168
156 for (p = paths; p; p = p->name_next) { 169 parameter_list_elem *last = NULL;
157 if (!strcmp(p->name, "/tmp")) 170 for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) {
158 found = 1; 171 if (!strcmp(path->name, "/tmp")) {
159 if (p->name_next) 172 found = true;
160 prev = p; 173 }
161 else 174 if (path->next) {
162 last = p; 175 prev = path;
176 } else {
177 last = path;
178 }
163 } 179 }
164 ok(found == 0, "/tmp element successfully deleted"); 180 ok(!found, "/tmp element successfully deleted");
165 181
166 p = np_del_parameter(last, prev); 182 int count = 0;
167 for (p = paths; p; p = p->name_next) { 183 mp_int_fs_list_del(&test_paths, p);
168 if (!strcmp(p->name, "/home")) 184 for (p = test_paths.first; p; p = p->next) {
169 found = 1; 185 if (!strcmp(p->name, "/home")) {
186 found = true;
187 }
170 last = p; 188 last = p;
171 count++; 189 count++;
172 } 190 }
173 ok(found == 0, "last (/home) element successfully deleted"); 191 ok(!found, "last (/home) element successfully deleted");
174 ok(count == 2, "two elements remaining"); 192 ok(count == 2, "two elements remaining");
175 193
176 return exit_status(); 194 return exit_status();
177} 195}
178 196
179void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) { 197void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags,
180 int matches = 0; 198 int expect, char *desc) {
181 regex_t re; 199 regex_t regex;
182 struct mount_entry *me; 200 if (regcomp(&regex, regstr, cflags) == 0) {
183 if (regcomp(&re, regstr, cflags) == 0) { 201 int matches = 0;
184 for (me = dummy_mount_list; me; me = me->me_next) { 202 for (struct mount_entry *me = dummy_mount_list; me; me = me->me_next) {
185 if (np_regex_match_mount_entry(me, &re)) 203 if (np_regex_match_mount_entry(me, &regex)) {
186 matches++; 204 matches++;
205 }
187 } 206 }
188 ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches); 207 ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect,
208 matches);
189 209
190 } else 210 } else {
191 ok(false, "regex '%s' not compilable", regstr); 211 ok(false, "regex '%s' not compilable", regstr);
212 }
192} 213}
diff --git a/plugins/tests/test_check_disk.t b/plugins/tests/test_check_disk.t
new file mode 100755
index 00000000..56354650
--- /dev/null
+++ b/plugins/tests/test_check_disk.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_disk") {
4 plan skip_all => "./test_check_disk not compiled - please enable libtap library to test";
5}
6exec "./test_check_disk";
diff --git a/plugins/tests/test_check_snmp.c b/plugins/tests/test_check_snmp.c
new file mode 100644
index 00000000..d71706d0
--- /dev/null
+++ b/plugins/tests/test_check_snmp.c
@@ -0,0 +1,175 @@
1/*****************************************************************************
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 *
17 *****************************************************************************/
18
19#include "tap.h"
20#include "../../config.h"
21
22#include <unistd.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25
26#include "utils_base.c"
27#include "../check_snmp.d/check_snmp_helpers.h"
28
29char *_np_state_generate_key(int argc, char **argv);
30char *_np_state_calculate_location_prefix(void);
31
32int main(int argc, char **argv) {
33 char *temp_string = (char *)_np_state_generate_key(argc, argv);
34 ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"),
35 "Got hash with exe and no parameters") ||
36 diag("You are probably running in wrong directory. Must run as ./test_utils");
37
38 int fake_argc = 4;
39 char *fake_argv[] = {
40 "./test_utils",
41 "here",
42 "--and",
43 "now",
44 };
45 temp_string = (char *)_np_state_generate_key(fake_argc, fake_argv);
46 ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"),
47 "Got based on expected argv");
48
49 unsetenv("MP_STATE_PATH");
50 temp_string = (char *)_np_state_calculate_location_prefix();
51 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory");
52
53 setenv("MP_STATE_PATH", "", 1);
54 temp_string = (char *)_np_state_calculate_location_prefix();
55 ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string");
56
57 setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1);
58 temp_string = (char *)_np_state_calculate_location_prefix();
59 ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory");
60
61 fake_argc = 1;
62 fake_argv[0] = "./test_utils";
63 state_key temp_state_key1 = np_enable_state(NULL, 51, "check_test", fake_argc, fake_argv);
64 ok(!strcmp(temp_state_key1.plugin_name, "check_test"), "Got plugin name");
65 ok(!strcmp(temp_state_key1.name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"),
66 "Got generated filename");
67
68 state_key temp_state_key2 =
69 np_enable_state("allowedchars_in_keyname", 77, "check_snmp", fake_argc, fake_argv);
70
71 char state_path[1024];
72 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname",
73 (unsigned long)geteuid());
74 ok(!strcmp(temp_state_key2.plugin_name, "check_test"), "Got plugin name");
75 ok(!strcmp(temp_state_key2.name, "allowedchars_in_keyname"), "Got key name with valid chars");
76 ok(!strcmp(temp_state_key2._filename, state_path), "Got internal filename");
77
78 /* Don't do this test just yet. Will die */
79 /*
80 np_enable_state("bad^chars$in@here", 77);
81 temp_state_key = this_monitoring_plugin->state;
82 ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced"
83 );
84 */
85
86 state_key temp_state_key3 =
87 np_enable_state("funnykeyname", 54, "check_snmp", fake_argc, fake_argv);
88 sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname",
89 (unsigned long)geteuid());
90 ok(!strcmp(temp_state_key3.plugin_name, "check_test"), "Got plugin name");
91 ok(!strcmp(temp_state_key3.name, "funnykeyname"), "Got key name");
92
93 ok(!strcmp(temp_state_key3._filename, state_path), "Got internal filename");
94 ok(temp_state_key3.data_version == 54, "Version set");
95
96 state_data *temp_state_data = np_state_read(temp_state_key3);
97 ok(temp_state_data == NULL, "Got no state data as file does not exist");
98
99 /*
100 temp_fp = fopen("var/statefile", "r");
101 if (temp_fp==NULL)
102 printf("Error opening. errno=%d\n", errno);
103 printf("temp_fp=%s\n", temp_fp);
104 ok( _np_state_read_file(temp_fp) == true, "Can read state file" );
105 fclose(temp_fp);
106 */
107
108 temp_state_key3._filename = "var/statefile";
109 temp_state_data = np_state_read(temp_state_key3);
110 ok(temp_state_data != NULL, "Got state data now") ||
111 diag("Are you running in right directory? Will get coredump next if not");
112 ok(temp_state_data->time == 1234567890, "Got time");
113 ok(!strcmp((char *)temp_state_data->data, "String to read"), "Data as expected");
114
115 temp_state_key3.data_version = 53;
116 temp_state_data = np_state_read(temp_state_key3);
117 ok(temp_state_data == NULL, "Older data version gives NULL");
118 temp_state_key3.data_version = 54;
119
120 temp_state_key3._filename = "var/nonexistent";
121 temp_state_data = np_state_read(temp_state_key3);
122 ok(temp_state_data == NULL, "Missing file gives NULL");
123
124 temp_state_key3._filename = "var/oldformat";
125 temp_state_data = np_state_read(temp_state_key3);
126 ok(temp_state_data == NULL, "Old file format gives NULL");
127
128 temp_state_key3._filename = "var/baddate";
129 temp_state_data = np_state_read(temp_state_key3);
130 ok(temp_state_data == NULL, "Bad date gives NULL");
131
132 temp_state_key3._filename = "var/missingdataline";
133 temp_state_data = np_state_read(temp_state_key3);
134 ok(temp_state_data == NULL, "Missing data line gives NULL");
135
136 unlink("var/generated");
137 temp_state_key3._filename = "var/generated";
138
139 time_t current_time = 1234567890;
140 np_state_write_string(temp_state_key3, current_time, "String to read");
141 ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected");
142
143 unlink("var/generated_directory/statefile");
144 unlink("var/generated_directory");
145 temp_state_key3._filename = "var/generated_directory/statefile";
146 current_time = 1234567890;
147 np_state_write_string(temp_state_key3, current_time, "String to read");
148 ok(system("cmp var/generated_directory/statefile var/statefile") == 0,
149 "Have created directory");
150
151 /* This test to check cannot write to dir - can't automate yet */
152 /*
153 unlink("var/generated_bad_dir");
154 mkdir("var/generated_bad_dir", S_IRUSR);
155 np_state_write_string(current_time, "String to read");
156 */
157
158 temp_state_key3._filename = "var/generated";
159 time(&current_time);
160 np_state_write_string(temp_state_key3, 0, "String to read");
161 temp_state_data = np_state_read(temp_state_key3);
162 /* Check time is set to current_time */
163 ok(system("cmp var/generated var/statefile > /dev/null") != 0,
164 "Generated file should be different this time");
165 ok(temp_state_data->time - current_time <= 1, "Has time generated from current time");
166
167 /* Don't know how to automatically test this. Need to be able to redefine die and catch the
168 * error */
169 /*
170 temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write";
171 np_state_write_string(0, "Bad file");
172 */
173
174 np_cleanup();
175}
diff --git a/plugins/tests/test_check_snmp.t b/plugins/tests/test_check_snmp.t
new file mode 100755
index 00000000..967633e9
--- /dev/null
+++ b/plugins/tests/test_check_snmp.t
@@ -0,0 +1,6 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_check_snmp") {
4 plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test";
5}
6exec "./test_check_snmp";
diff --git a/plugins/tests/test_check_swap.c b/plugins/tests/test_check_swap.c
index b85fb4ad..94d56ce7 100644
--- a/plugins/tests/test_check_swap.c
+++ b/plugins/tests/test_check_swap.c
@@ -5,9 +5,7 @@
5int verbose = 0; 5int verbose = 0;
6 6
7void print_usage(void) {} 7void print_usage(void) {}
8void print_help(swap_config config) { 8void print_help(swap_config config) { (void)config; }
9 (void) config;
10}
11 9
12const char *progname = "test_check_swap"; 10const char *progname = "test_check_swap";
13 11
diff --git a/plugins/urlize.c b/plugins/urlize.c
index 1aa4e425..a8590fae 100644
--- a/plugins/urlize.c
+++ b/plugins/urlize.c
@@ -53,8 +53,10 @@ int main(int argc, char **argv) {
53 53
54 int c; 54 int c;
55 int option = 0; 55 int option = 0;
56 static struct option longopts[] = { 56 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
57 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"url", required_argument, 0, 'u'}, {0, 0, 0, 0}}; 57 {"version", no_argument, 0, 'V'},
58 {"url", required_argument, 0, 'u'},
59 {0, 0, 0, 0}};
58 60
59 setlocale(LC_ALL, ""); 61 setlocale(LC_ALL, "");
60 bindtextdomain(PACKAGE, LOCALEDIR); 62 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -69,8 +71,9 @@ int main(int argc, char **argv) {
69 while (1) { 71 while (1) {
70 c = getopt_long(argc, argv, "+hVu:", longopts, &option); 72 c = getopt_long(argc, argv, "+hVu:", longopts, &option);
71 73
72 if (c == -1 || c == EOF) 74 if (c == -1 || c == EOF) {
73 break; 75 break;
76 }
74 77
75 switch (c) { 78 switch (c) {
76 case 'h': /* help */ 79 case 'h': /* help */
@@ -90,8 +93,9 @@ int main(int argc, char **argv) {
90 } 93 }
91 } 94 }
92 95
93 if (url == NULL) 96 if (url == NULL) {
94 url = strdup(argv[optind++]); 97 url = strdup(argv[optind++]);
98 }
95 99
96 cmd = strdup(argv[optind++]); 100 cmd = strdup(argv[optind++]);
97 for (c = optind; c < argc; c++) { 101 for (c = optind; c < argc; c++) {
@@ -118,27 +122,32 @@ int main(int argc, char **argv) {
118 strcat(tstr, buf); 122 strcat(tstr, buf);
119 } 123 }
120 124
121 if (!found) 125 if (!found) {
122 die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0], cmd); 126 die(STATE_UNKNOWN, _("%s UNKNOWN - No data received from host\nCMD: %s</A>\n"), argv[0],
127 cmd);
128 }
123 129
124 /* chop the newline character */ 130 /* chop the newline character */
125 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) 131 if ((nstr = strchr(tstr, NEWLINE_CHARACTER)) != NULL) {
126 *nstr = '\0'; 132 *nstr = '\0';
133 }
127 134
128 /* tokenize the string for Perfdata if there is some */ 135 /* tokenize the string for Perfdata if there is some */
129 nstr = strtok(tstr, PERF_CHARACTER); 136 nstr = strtok(tstr, PERF_CHARACTER);
130 printf("%s", nstr); 137 printf("%s", nstr);
131 printf("</A>"); 138 printf("</A>");
132 nstr = strtok(NULL, PERF_CHARACTER); 139 nstr = strtok(NULL, PERF_CHARACTER);
133 if (nstr != NULL) 140 if (nstr != NULL) {
134 printf(" | %s", nstr); 141 printf(" | %s", nstr);
142 }
135 143
136 /* close the pipe */ 144 /* close the pipe */
137 result = spclose(child_process); 145 result = spclose(child_process);
138 146
139 /* WARNING if output found on stderr */ 147 /* WARNING if output found on stderr */
140 if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) 148 if (fgets(buf, MAX_INPUT_BUFFER - 1, child_stderr)) {
141 result = max_state(result, STATE_WARNING); 149 result = max_state(result, STATE_WARNING);
150 }
142 151
143 /* close stderr */ 152 /* close stderr */
144 (void)fclose(child_stderr); 153 (void)fclose(child_stderr);
@@ -153,8 +162,10 @@ void print_help(void) {
153 printf(COPYRIGHT, copyright, email); 162 printf(COPYRIGHT, copyright, email);
154 163
155 printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>")); 164 printf("%s\n", _("This plugin wraps the text output of another command (plugin) in HTML <A>"));
156 printf("%s\n", _("tags, thus displaying the child plugin's output as a clickable link in compatible")); 165 printf("%s\n",
157 printf("%s\n", _("monitoring status screen. This plugin returns the status of the invoked plugin.")); 166 _("tags, thus displaying the child plugin's output as a clickable link in compatible"));
167 printf("%s\n",
168 _("monitoring status screen. This plugin returns the status of the invoked plugin."));
158 169
159 printf("\n\n"); 170 printf("\n\n");
160 171
@@ -164,7 +175,8 @@ void print_help(void) {
164 175
165 printf("\n"); 176 printf("\n");
166 printf("%s\n", _("Examples:")); 177 printf("%s\n", _("Examples:"));
167 printf("%s\n", _("Pay close attention to quoting to ensure that the shell passes the expected")); 178 printf("%s\n",
179 _("Pay close attention to quoting to ensure that the shell passes the expected"));
168 printf("%s\n\n", _("data to the plugin. For example, in:")); 180 printf("%s\n\n", _("data to the plugin. For example, in:"));
169 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'")); 181 printf(" %s\n\n", _("urlize http://example.com/ check_http -H example.com -r 'two words'"));
170 printf(" %s\n", _("the shell will remove the single quotes and urlize will see:")); 182 printf(" %s\n", _("the shell will remove the single quotes and urlize will see:"));
diff --git a/plugins/utils.c b/plugins/utils.c
index 6d366e3d..41fe5fcf 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -42,54 +42,6 @@ extern const char *progname;
42 42
43time_t start_time, end_time; 43time_t start_time, end_time;
44 44
45/* **************************************************************************
46 * max_state(STATE_x, STATE_y)
47 * compares STATE_x to STATE_y and returns result based on the following
48 * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
49 *
50 * Note that numerically the above does not hold
51 ****************************************************************************/
52
53int max_state(int a, int b) {
54 if (a == STATE_CRITICAL || b == STATE_CRITICAL)
55 return STATE_CRITICAL;
56 else if (a == STATE_WARNING || b == STATE_WARNING)
57 return STATE_WARNING;
58 else if (a == STATE_OK || b == STATE_OK)
59 return STATE_OK;
60 else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
61 return STATE_UNKNOWN;
62 else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
63 return STATE_DEPENDENT;
64 else
65 return max(a, b);
66}
67
68/* **************************************************************************
69 * max_state_alt(STATE_x, STATE_y)
70 * compares STATE_x to STATE_y and returns result based on the following
71 * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
72 *
73 * The main difference between max_state_alt and max_state it that it doesn't
74 * allow setting a default to UNKNOWN. It will instead prioritixe any valid
75 * non-OK state.
76 ****************************************************************************/
77
78int max_state_alt(int a, int b) {
79 if (a == STATE_CRITICAL || b == STATE_CRITICAL)
80 return STATE_CRITICAL;
81 else if (a == STATE_WARNING || b == STATE_WARNING)
82 return STATE_WARNING;
83 else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
84 return STATE_UNKNOWN;
85 else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
86 return STATE_DEPENDENT;
87 else if (a == STATE_OK || b == STATE_OK)
88 return STATE_OK;
89 else
90 return max(a, b);
91}
92
93void usage(const char *msg) { 45void usage(const char *msg) {
94 printf("%s\n", msg); 46 printf("%s\n", msg);
95 print_usage(); 47 print_usage();
@@ -137,41 +89,46 @@ bool is_numeric(char *number) {
137 char tmp[1]; 89 char tmp[1];
138 float x; 90 float x;
139 91
140 if (!number) 92 if (!number) {
141 return false; 93 return false;
142 else if (sscanf(number, "%f%c", &x, tmp) == 1) 94 } else if (sscanf(number, "%f%c", &x, tmp) == 1) {
143 return true; 95 return true;
144 else 96 } else {
145 return false; 97 return false;
98 }
146} 99}
147 100
148bool is_positive(char *number) { 101bool is_positive(char *number) {
149 if (is_numeric(number) && atof(number) > 0.0) 102 if (is_numeric(number) && atof(number) > 0.0) {
150 return true; 103 return true;
151 else 104 } else {
152 return false; 105 return false;
106 }
153} 107}
154 108
155bool is_negative(char *number) { 109bool is_negative(char *number) {
156 if (is_numeric(number) && atof(number) < 0.0) 110 if (is_numeric(number) && atof(number) < 0.0) {
157 return true; 111 return true;
158 else 112 } else {
159 return false; 113 return false;
114 }
160} 115}
161 116
162bool is_nonnegative(char *number) { 117bool is_nonnegative(char *number) {
163 if (is_numeric(number) && atof(number) >= 0.0) 118 if (is_numeric(number) && atof(number) >= 0.0) {
164 return true; 119 return true;
165 else 120 } else {
166 return false; 121 return false;
122 }
167} 123}
168 124
169bool is_percentage(char *number) { 125bool is_percentage(char *number) {
170 int x; 126 int x;
171 if (is_numeric(number) && (x = atof(number)) >= 0 && x <= 100) 127 if (is_numeric(number) && (x = atof(number)) >= 0 && x <= 100) {
172 return true; 128 return true;
173 else 129 } else {
174 return false; 130 return false;
131 }
175} 132}
176 133
177bool is_percentage_expression(const char str[]) { 134bool is_percentage_expression(const char str[]) {
@@ -204,36 +161,41 @@ bool is_percentage_expression(const char str[]) {
204bool is_integer(char *number) { 161bool is_integer(char *number) {
205 long int n; 162 long int n;
206 163
207 if (!number || (strspn(number, "-0123456789 ") != strlen(number))) 164 if (!number || (strspn(number, "-0123456789 ") != strlen(number))) {
208 return false; 165 return false;
166 }
209 167
210 n = strtol(number, NULL, 10); 168 n = strtol(number, NULL, 10);
211 169
212 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) 170 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX) {
213 return true; 171 return true;
214 else 172 } else {
215 return false; 173 return false;
174 }
216} 175}
217 176
218bool is_intpos(char *number) { 177bool is_intpos(char *number) {
219 if (is_integer(number) && atoi(number) > 0) 178 if (is_integer(number) && atoi(number) > 0) {
220 return true; 179 return true;
221 else 180 } else {
222 return false; 181 return false;
182 }
223} 183}
224 184
225bool is_intneg(char *number) { 185bool is_intneg(char *number) {
226 if (is_integer(number) && atoi(number) < 0) 186 if (is_integer(number) && atoi(number) < 0) {
227 return true; 187 return true;
228 else 188 } else {
229 return false; 189 return false;
190 }
230} 191}
231 192
232bool is_intnonneg(char *number) { 193bool is_intnonneg(char *number) {
233 if (is_integer(number) && atoi(number) >= 0) 194 if (is_integer(number) && atoi(number) >= 0) {
234 return true; 195 return true;
235 else 196 } else {
236 return false; 197 return false;
198 }
237} 199}
238 200
239/* 201/*
@@ -295,19 +257,21 @@ bool is_uint64(char *number, uint64_t *target) {
295 257
296bool is_intpercent(char *number) { 258bool is_intpercent(char *number) {
297 int i; 259 int i;
298 if (is_integer(number) && (i = atoi(number)) >= 0 && i <= 100) 260 if (is_integer(number) && (i = atoi(number)) >= 0 && i <= 100) {
299 return true; 261 return true;
300 else 262 } else {
301 return false; 263 return false;
264 }
302} 265}
303 266
304bool is_option(char *str) { 267bool is_option(char *str) {
305 if (!str) 268 if (!str) {
306 return false; 269 return false;
307 else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) 270 } else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) {
308 return true; 271 return true;
309 else 272 } else {
310 return false; 273 return false;
274 }
311} 275}
312 276
313#ifdef NEED_GETTIMEOFDAY 277#ifdef NEED_GETTIMEOFDAY
@@ -321,7 +285,8 @@ double delta_time(struct timeval tv) {
321 struct timeval now; 285 struct timeval now;
322 286
323 gettimeofday(&now, NULL); 287 gettimeofday(&now, NULL);
324 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);
325} 290}
326 291
327long deltime(struct timeval tv) { 292long deltime(struct timeval tv) {
@@ -336,10 +301,11 @@ void strip(char *buffer) {
336 301
337 for (x = strlen(buffer); x >= 1; x--) { 302 for (x = strlen(buffer); x >= 1; x--) {
338 i = x - 1; 303 i = x - 1;
339 if (buffer[i] == ' ' || buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t') 304 if (buffer[i] == ' ' || buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\t') {
340 buffer[i] = '\0'; 305 buffer[i] = '\0';
341 else 306 } else {
342 break; 307 break;
308 }
343 } 309 }
344 return; 310 return;
345} 311}
@@ -357,8 +323,9 @@ void strip(char *buffer) {
357 *****************************************************************************/ 323 *****************************************************************************/
358 324
359char *strscpy(char *dest, const char *src) { 325char *strscpy(char *dest, const char *src) {
360 if (src == NULL) 326 if (src == NULL) {
361 return NULL; 327 return NULL;
328 }
362 329
363 xasprintf(&dest, "%s", src); 330 xasprintf(&dest, "%s", src);
364 331
@@ -417,17 +384,21 @@ char *strscpy(char *dest, const char *src) {
417 384
418char *strnl(char *str) { 385char *strnl(char *str) {
419 size_t len; 386 size_t len;
420 if (str == NULL) 387 if (str == NULL) {
421 return NULL; 388 return NULL;
389 }
422 str = strpbrk(str, "\r\n"); 390 str = strpbrk(str, "\r\n");
423 if (str == NULL) 391 if (str == NULL) {
424 return NULL; 392 return NULL;
393 }
425 len = strspn(str, "\r\n"); 394 len = strspn(str, "\r\n");
426 if (str[len] == '\0') 395 if (str[len] == '\0') {
427 return NULL; 396 return NULL;
397 }
428 str += len; 398 str += len;
429 if (strlen(str) == 0) 399 if (strlen(str) == 0) {
430 return NULL; 400 return NULL;
401 }
431 return str; 402 return str;
432} 403}
433 404
@@ -450,15 +421,18 @@ char *strnl(char *str) {
450char *strpcpy(char *dest, const char *src, const char *str) { 421char *strpcpy(char *dest, const char *src, const char *str) {
451 size_t len; 422 size_t len;
452 423
453 if (src) 424 if (src) {
454 len = strcspn(src, str); 425 len = strcspn(src, str);
455 else 426 } else {
456 return NULL; 427 return NULL;
428 }
457 429
458 if (dest == NULL || strlen(dest) < len) 430 if (dest == NULL || strlen(dest) < len) {
459 dest = realloc(dest, len + 1); 431 dest = realloc(dest, len + 1);
460 if (dest == NULL) 432 }
433 if (dest == NULL) {
461 die(STATE_UNKNOWN, _("failed realloc in strpcpy\n")); 434 die(STATE_UNKNOWN, _("failed realloc in strpcpy\n"));
435 }
462 436
463 strncpy(dest, src, len); 437 strncpy(dest, src, len);
464 dest[len] = '\0'; 438 dest[len] = '\0';
@@ -482,10 +456,11 @@ char *strpcpy(char *dest, const char *src, const char *str) {
482char *strpcat(char *dest, const char *src, const char *str) { 456char *strpcat(char *dest, const char *src, const char *str) {
483 size_t len, l2; 457 size_t len, l2;
484 458
485 if (dest) 459 if (dest) {
486 len = strlen(dest); 460 len = strlen(dest);
487 else 461 } else {
488 len = 0; 462 len = 0;
463 }
489 464
490 if (src) { 465 if (src) {
491 l2 = strcspn(src, str); 466 l2 = strcspn(src, str);
@@ -494,8 +469,9 @@ char *strpcat(char *dest, const char *src, const char *str) {
494 } 469 }
495 470
496 dest = realloc(dest, len + l2 + 1); 471 dest = realloc(dest, len + l2 + 1);
497 if (dest == NULL) 472 if (dest == NULL) {
498 die(STATE_UNKNOWN, _("failed malloc in strscat\n")); 473 die(STATE_UNKNOWN, _("failed malloc in strscat\n"));
474 }
499 475
500 strncpy(dest + len, src, l2); 476 strncpy(dest + len, src, l2);
501 dest[len + l2] = '\0'; 477 dest[len + l2] = '\0';
@@ -511,8 +487,9 @@ char *strpcat(char *dest, const char *src, const char *str) {
511 487
512int xvasprintf(char **strp, const char *fmt, va_list ap) { 488int xvasprintf(char **strp, const char *fmt, va_list ap) {
513 int result = vasprintf(strp, fmt, ap); 489 int result = vasprintf(strp, fmt, ap);
514 if (result == -1 || *strp == NULL) 490 if (result == -1 || *strp == NULL) {
515 die(STATE_UNKNOWN, _("failed malloc in xvasprintf\n")); 491 die(STATE_UNKNOWN, _("failed malloc in xvasprintf\n"));
492 }
516 return result; 493 return result;
517} 494}
518 495
@@ -531,126 +508,147 @@ int xasprintf(char **strp, const char *fmt, ...) {
531 * 508 *
532 ******************************************************************************/ 509 ******************************************************************************/
533 510
534char *perfdata(const char *label, long int val, const char *uom, int warnp, long int warn, int critp, long int crit, int minp, 511char *perfdata(const char *label, long int val, const char *uom, bool warnp, long int warn,
535 long int minv, int maxp, long int maxv) { 512 bool critp, long int crit, bool minp, long int minv, bool maxp, long int maxv) {
536 char *data = NULL; 513 char *data = NULL;
537 514
538 if (strpbrk(label, "'= ")) 515 if (strpbrk(label, "'= ")) {
539 xasprintf(&data, "'%s'=%ld%s;", label, val, uom); 516 xasprintf(&data, "'%s'=%ld%s;", label, val, uom);
540 else 517 } else {
541 xasprintf(&data, "%s=%ld%s;", label, val, uom); 518 xasprintf(&data, "%s=%ld%s;", label, val, uom);
519 }
542 520
543 if (warnp) 521 if (warnp) {
544 xasprintf(&data, "%s%ld;", data, warn); 522 xasprintf(&data, "%s%ld;", data, warn);
545 else 523 } else {
546 xasprintf(&data, "%s;", data); 524 xasprintf(&data, "%s;", data);
525 }
547 526
548 if (critp) 527 if (critp) {
549 xasprintf(&data, "%s%ld;", data, crit); 528 xasprintf(&data, "%s%ld;", data, crit);
550 else 529 } else {
551 xasprintf(&data, "%s;", data); 530 xasprintf(&data, "%s;", data);
531 }
552 532
553 if (minp) 533 if (minp) {
554 xasprintf(&data, "%s%ld;", data, minv); 534 xasprintf(&data, "%s%ld;", data, minv);
555 else 535 } else {
556 xasprintf(&data, "%s;", data); 536 xasprintf(&data, "%s;", data);
537 }
557 538
558 if (maxp) 539 if (maxp) {
559 xasprintf(&data, "%s%ld", data, maxv); 540 xasprintf(&data, "%s%ld", data, maxv);
541 }
560 542
561 return data; 543 return data;
562} 544}
563 545
564char *perfdata_uint64(const char *label, uint64_t val, const char *uom, int warnp, /* Warning present */ 546char *perfdata_uint64(const char *label, uint64_t val, const char *uom,
565 uint64_t warn, int critp, /* Critical present */ 547 bool warnp, /* Warning present */
566 uint64_t crit, int minp, /* Minimum present */ 548 uint64_t warn, bool critp, /* Critical present */
567 uint64_t minv, int maxp, /* Maximum present */ 549 uint64_t crit, bool minp, /* Minimum present */
550 uint64_t minv, bool maxp, /* Maximum present */
568 uint64_t maxv) { 551 uint64_t maxv) {
569 char *data = NULL; 552 char *data = NULL;
570 553
571 if (strpbrk(label, "'= ")) 554 if (strpbrk(label, "'= ")) {
572 xasprintf(&data, "'%s'=%" PRIu64 "%s;", label, val, uom); 555 xasprintf(&data, "'%s'=%" PRIu64 "%s;", label, val, uom);
573 else 556 } else {
574 xasprintf(&data, "%s=%" PRIu64 "%s;", label, val, uom); 557 xasprintf(&data, "%s=%" PRIu64 "%s;", label, val, uom);
558 }
575 559
576 if (warnp) 560 if (warnp) {
577 xasprintf(&data, "%s%" PRIu64 ";", data, warn); 561 xasprintf(&data, "%s%" PRIu64 ";", data, warn);
578 else 562 } else {
579 xasprintf(&data, "%s;", data); 563 xasprintf(&data, "%s;", data);
564 }
580 565
581 if (critp) 566 if (critp) {
582 xasprintf(&data, "%s%" PRIu64 ";", data, crit); 567 xasprintf(&data, "%s%" PRIu64 ";", data, crit);
583 else 568 } else {
584 xasprintf(&data, "%s;", data); 569 xasprintf(&data, "%s;", data);
570 }
585 571
586 if (minp) 572 if (minp) {
587 xasprintf(&data, "%s%" PRIu64 ";", data, minv); 573 xasprintf(&data, "%s%" PRIu64 ";", data, minv);
588 else 574 } else {
589 xasprintf(&data, "%s;", data); 575 xasprintf(&data, "%s;", data);
576 }
590 577
591 if (maxp) 578 if (maxp) {
592 xasprintf(&data, "%s%" PRIu64, data, maxv); 579 xasprintf(&data, "%s%" PRIu64, data, maxv);
580 }
593 581
594 return data; 582 return data;
595} 583}
596 584
597char *perfdata_int64(const char *label, int64_t val, const char *uom, int warnp, /* Warning present */ 585char *perfdata_int64(const char *label, int64_t val, const char *uom,
598 int64_t warn, int critp, /* Critical present */ 586 bool warnp, /* Warning present */
599 int64_t crit, int minp, /* Minimum present */ 587 int64_t warn, bool critp, /* Critical present */
600 int64_t minv, int maxp, /* Maximum present */ 588 int64_t crit, bool minp, /* Minimum present */
589 int64_t minv, bool maxp, /* Maximum present */
601 int64_t maxv) { 590 int64_t maxv) {
602 char *data = NULL; 591 char *data = NULL;
603 592
604 if (strpbrk(label, "'= ")) 593 if (strpbrk(label, "'= ")) {
605 xasprintf(&data, "'%s'=%" PRId64 "%s;", label, val, uom); 594 xasprintf(&data, "'%s'=%" PRId64 "%s;", label, val, uom);
606 else 595 } else {
607 xasprintf(&data, "%s=%" PRId64 "%s;", label, val, uom); 596 xasprintf(&data, "%s=%" PRId64 "%s;", label, val, uom);
597 }
608 598
609 if (warnp) 599 if (warnp) {
610 xasprintf(&data, "%s%" PRId64 ";", data, warn); 600 xasprintf(&data, "%s%" PRId64 ";", data, warn);
611 else 601 } else {
612 xasprintf(&data, "%s;", data); 602 xasprintf(&data, "%s;", data);
603 }
613 604
614 if (critp) 605 if (critp) {
615 xasprintf(&data, "%s%" PRId64 ";", data, crit); 606 xasprintf(&data, "%s%" PRId64 ";", data, crit);
616 else 607 } else {
617 xasprintf(&data, "%s;", data); 608 xasprintf(&data, "%s;", data);
609 }
618 610
619 if (minp) 611 if (minp) {
620 xasprintf(&data, "%s%" PRId64 ";", data, minv); 612 xasprintf(&data, "%s%" PRId64 ";", data, minv);
621 else 613 } else {
622 xasprintf(&data, "%s;", data); 614 xasprintf(&data, "%s;", data);
615 }
623 616
624 if (maxp) 617 if (maxp) {
625 xasprintf(&data, "%s%" PRId64, data, maxv); 618 xasprintf(&data, "%s%" PRId64, data, maxv);
619 }
626 620
627 return data; 621 return data;
628} 622}
629 623
630char *fperfdata(const char *label, double val, const char *uom, int warnp, double warn, int critp, double crit, int minp, double minv, 624char *fperfdata(const char *label, double val, const char *uom, bool warnp, double warn, bool critp,
631 int maxp, double maxv) { 625 double crit, bool minp, double minv, bool maxp, double maxv) {
632 char *data = NULL; 626 char *data = NULL;
633 627
634 if (strpbrk(label, "'= ")) 628 if (strpbrk(label, "'= ")) {
635 xasprintf(&data, "'%s'=", label); 629 xasprintf(&data, "'%s'=", label);
636 else 630 } else {
637 xasprintf(&data, "%s=", label); 631 xasprintf(&data, "%s=", label);
632 }
638 633
639 xasprintf(&data, "%s%f", data, val); 634 xasprintf(&data, "%s%f", data, val);
640 xasprintf(&data, "%s%s;", data, uom); 635 xasprintf(&data, "%s%s;", data, uom);
641 636
642 if (warnp) 637 if (warnp) {
643 xasprintf(&data, "%s%f", data, warn); 638 xasprintf(&data, "%s%f", data, warn);
639 }
644 640
645 xasprintf(&data, "%s;", data); 641 xasprintf(&data, "%s;", data);
646 642
647 if (critp) 643 if (critp) {
648 xasprintf(&data, "%s%f", data, crit); 644 xasprintf(&data, "%s%f", data, crit);
645 }
649 646
650 xasprintf(&data, "%s;", data); 647 xasprintf(&data, "%s;", data);
651 648
652 if (minp) 649 if (minp) {
653 xasprintf(&data, "%s%f", data, minv); 650 xasprintf(&data, "%s%f", data, minv);
651 }
654 652
655 if (maxp) { 653 if (maxp) {
656 xasprintf(&data, "%s;", data); 654 xasprintf(&data, "%s;", data);
@@ -660,28 +658,33 @@ char *fperfdata(const char *label, double val, const char *uom, int warnp, doubl
660 return data; 658 return data;
661} 659}
662 660
663char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, int minp, double minv, int maxp, double maxv) { 661char *sperfdata(const char *label, double val, const char *uom, char *warn, char *crit, bool minp,
662 double minv, bool maxp, double maxv) {
664 char *data = NULL; 663 char *data = NULL;
665 if (strpbrk(label, "'= ")) 664 if (strpbrk(label, "'= ")) {
666 xasprintf(&data, "'%s'=", label); 665 xasprintf(&data, "'%s'=", label);
667 else 666 } else {
668 xasprintf(&data, "%s=", label); 667 xasprintf(&data, "%s=", label);
668 }
669 669
670 xasprintf(&data, "%s%f", data, val); 670 xasprintf(&data, "%s%f", data, val);
671 xasprintf(&data, "%s%s;", data, uom); 671 xasprintf(&data, "%s%s;", data, uom);
672 672
673 if (warn != NULL) 673 if (warn != NULL) {
674 xasprintf(&data, "%s%s", data, warn); 674 xasprintf(&data, "%s%s", data, warn);
675 }
675 676
676 xasprintf(&data, "%s;", data); 677 xasprintf(&data, "%s;", data);
677 678
678 if (crit != NULL) 679 if (crit != NULL) {
679 xasprintf(&data, "%s%s", data, crit); 680 xasprintf(&data, "%s%s", data, crit);
681 }
680 682
681 xasprintf(&data, "%s;", data); 683 xasprintf(&data, "%s;", data);
682 684
683 if (minp) 685 if (minp) {
684 xasprintf(&data, "%s%f", data, minv); 686 xasprintf(&data, "%s%f", data, minv);
687 }
685 688
686 if (maxp) { 689 if (maxp) {
687 xasprintf(&data, "%s;", data); 690 xasprintf(&data, "%s;", data);
@@ -691,28 +694,33 @@ char *sperfdata(const char *label, double val, const char *uom, char *warn, char
691 return data; 694 return data;
692} 695}
693 696
694char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, int minp, int minv, int maxp, int maxv) { 697char *sperfdata_int(const char *label, int val, const char *uom, char *warn, char *crit, bool minp,
698 int minv, bool maxp, int maxv) {
695 char *data = NULL; 699 char *data = NULL;
696 if (strpbrk(label, "'= ")) 700 if (strpbrk(label, "'= ")) {
697 xasprintf(&data, "'%s'=", label); 701 xasprintf(&data, "'%s'=", label);
698 else 702 } else {
699 xasprintf(&data, "%s=", label); 703 xasprintf(&data, "%s=", label);
704 }
700 705
701 xasprintf(&data, "%s%d", data, val); 706 xasprintf(&data, "%s%d", data, val);
702 xasprintf(&data, "%s%s;", data, uom); 707 xasprintf(&data, "%s%s;", data, uom);
703 708
704 if (warn != NULL) 709 if (warn != NULL) {
705 xasprintf(&data, "%s%s", data, warn); 710 xasprintf(&data, "%s%s", data, warn);
711 }
706 712
707 xasprintf(&data, "%s;", data); 713 xasprintf(&data, "%s;", data);
708 714
709 if (crit != NULL) 715 if (crit != NULL) {
710 xasprintf(&data, "%s%s", data, crit); 716 xasprintf(&data, "%s%s", data, crit);
717 }
711 718
712 xasprintf(&data, "%s;", data); 719 xasprintf(&data, "%s;", data);
713 720
714 if (minp) 721 if (minp) {
715 xasprintf(&data, "%s%d", data, minv); 722 xasprintf(&data, "%s%d", data, minv);
723 }
716 724
717 if (maxp) { 725 if (maxp) {
718 xasprintf(&data, "%s;", data); 726 xasprintf(&data, "%s;", 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 */
diff --git a/tap/tap.c b/tap/tap.c
index 00ceab27..fb40736f 100644
--- a/tap/tap.c
+++ b/tap/tap.c
@@ -65,7 +65,8 @@ static void _cleanup(void);
65 * test_name -- the name of the test, may be NULL 65 * test_name -- the name of the test, may be NULL
66 * test_comment -- a comment to print afterwards, may be NULL 66 * test_comment -- a comment to print afterwards, may be NULL
67 */ 67 */
68unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line, char *test_name, ...) { 68unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line, char *test_name,
69 ...) {
69 va_list ap; 70 va_list ap;
70 char *local_test_name = NULL; 71 char *local_test_name = NULL;
71 char *c; 72 char *c;
@@ -95,7 +96,9 @@ unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line
95 } 96 }
96 97
97 if (name_is_digits) { 98 if (name_is_digits) {
98 diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name); 99 diag(
100 " You named your test '%s'. You shouldn't use numbers for your test names.",
101 local_test_name);
99 diag(" Very confusing."); 102 diag(" Very confusing.");
100 } 103 }
101 } 104 }
@@ -116,8 +119,9 @@ unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line
116 if (local_test_name != NULL) { 119 if (local_test_name != NULL) {
117 flockfile(stdout); 120 flockfile(stdout);
118 for (c = local_test_name; *c != '\0'; c++) { 121 for (c = local_test_name; *c != '\0'; c++) {
119 if (*c == '#') 122 if (*c == '#') {
120 fputc('\\', stdout); 123 fputc('\\', stdout);
124 }
121 fputc((int)*c, stdout); 125 fputc((int)*c, stdout);
122 } 126 }
123 funlockfile(stdout); 127 funlockfile(stdout);
@@ -135,14 +139,16 @@ unsigned int _gen_result(int ok, const char *func, char *file, unsigned int line
135 the test failed. */ 139 the test failed. */
136 if (todo) { 140 if (todo) {
137 printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); 141 printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
138 if (!ok) 142 if (!ok) {
139 failures--; 143 failures--;
144 }
140 } 145 }
141 146
142 printf("\n"); 147 printf("\n");
143 148
144 if (!ok) 149 if (!ok) {
145 diag(" Failed %stest (%s:%s() at line %d)", todo ? "(TODO) " : "", file, func, line); 150 diag(" Failed %stest (%s:%s() at line %d)", todo ? "(TODO) " : "", file, func, line);
151 }
146 152
147 free(local_test_name); 153 free(local_test_name);
148 154
@@ -212,8 +218,9 @@ int plan_skip_all(char *reason) {
212 218
213 printf("1..0"); 219 printf("1..0");
214 220
215 if (reason != NULL) 221 if (reason != NULL) {
216 printf(" # Skip %s", reason); 222 printf(" # Skip %s", reason);
223 }
217 224
218 printf("\n"); 225 printf("\n");
219 226
@@ -294,7 +301,8 @@ int skip(unsigned int n, char *fmt, ...) {
294 301
295 while (n-- > 0) { 302 while (n-- > 0) {
296 test_count++; 303 test_count++;
297 printf("ok %d # skip %s\n", test_count, skip_msg != NULL ? skip_msg : "libtap():malloc() failed"); 304 printf("ok %d # skip %s\n", test_count,
305 skip_msg != NULL ? skip_msg : "libtap():malloc() failed");
298 } 306 }
299 307
300 free(skip_msg); 308 free(skip_msg);
@@ -396,8 +404,9 @@ void _cleanup(void) {
396 return; 404 return;
397 } 405 }
398 406
399 if (failures) 407 if (failures) {
400 diag("Looks like you failed %d tests of %d.", failures, test_count); 408 diag("Looks like you failed %d tests of %d.", failures, test_count);
409 }
401 410
402 UNLOCK; 411 UNLOCK;
403} 412}
diff --git a/tap/tap.h b/tap/tap.h
index 5abbd76a..0c550bfd 100644
--- a/tap/tap.h
+++ b/tap/tap.h
@@ -28,45 +28,50 @@
28 and requires the caller to add the final comma if they've omitted 28 and requires the caller to add the final comma if they've omitted
29 the optional arguments */ 29 the optional arguments */
30#ifdef __GNUC__ 30#ifdef __GNUC__
31# define ok(e, test, ...) \ 31# define ok(e, test, ...) \
32 ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__) \ 32 ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__) \
33 : _gen_result(0, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__)) 33 : _gen_result(0, __func__, __FILE__, __LINE__, test, ##__VA_ARGS__))
34 34
35# define ok1(e) ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) 35# define ok1(e) \
36 ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) \
37 : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
36 38
37# define pass(test, ...) ok(1, test, ##__VA_ARGS__); 39# define pass(test, ...) ok(1, test, ##__VA_ARGS__);
38# define fail(test, ...) ok(0, test, ##__VA_ARGS__); 40# define fail(test, ...) ok(0, test, ##__VA_ARGS__);
39 41
40# define skip_start(test, n, fmt, ...) \ 42# define skip_start(test, n, fmt, ...) \
41 do { \ 43 do { \
42 if ((test)) { \ 44 if ((test)) { \
43 skip(n, fmt, ##__VA_ARGS__); \ 45 skip(n, fmt, ##__VA_ARGS__); \
44 continue; \ 46 continue; \
45 } 47 }
46#else /* __GNUC__ */ 48#else /* __GNUC__ */
47/* The original tap.h used to test if __STDC_VERSION__ >= 199901L here. This 49/* The original tap.h used to test if __STDC_VERSION__ >= 199901L here. This
48 * doesn't seem to work on HP-UX even though the code compile fine. I'm not 50 * doesn't seem to work on HP-UX even though the code compile fine. I'm not
49 * sure how to add an exception here for HP-UX so I just removed the check 51 * sure how to add an exception here for HP-UX so I just removed the check
50 * for now */ 52 * for now */
51# define ok(e, ...) \ 53# define ok(e, ...) \
52 ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, __VA_ARGS__) : _gen_result(0, __func__, __FILE__, __LINE__, __VA_ARGS__)) 54 ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, __VA_ARGS__) \
55 : _gen_result(0, __func__, __FILE__, __LINE__, __VA_ARGS__))
53 56
54# define ok1(e) ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) 57# define ok1(e) \
58 ((e) ? _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) \
59 : _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
55 60
56# define pass(...) ok(1, __VA_ARGS__); 61# define pass(...) ok(1, __VA_ARGS__);
57# define fail(...) ok(0, __VA_ARGS__); 62# define fail(...) ok(0, __VA_ARGS__);
58 63
59# define skip_start(test, n, ...) \ 64# define skip_start(test, n, ...) \
60 do { \ 65 do { \
61 if ((test)) { \ 66 if ((test)) { \
62 skip(n, __VA_ARGS__); \ 67 skip(n, __VA_ARGS__); \
63 continue; \ 68 continue; \
64 } 69 }
65#endif /* __GNUC__ */ 70#endif /* __GNUC__ */
66 71
67#define skip_end \ 72#define skip_end \
68 } \ 73 } \
69 while (0) \ 74 while (0) \
70 ; 75 ;
71 76
72unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...); 77unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
diff --git a/tools/mini_epn.c b/tools/mini_epn.c
index 6f3c5d02..1b09f1c1 100644
--- a/tools/mini_epn.c
+++ b/tools/mini_epn.c
@@ -1,14 +1,14 @@
1/* 1/*
2 * 2 *
3 * MINI_EPN.C - Mini Embedded Perl Nagios 3 * MINI_EPN.C - Mini Embedded Perl Nagios
4 * Contributed by Stanley Hopcroft 4 * Contributed by Stanley Hopcroft
5 * Modified by Douglas Warner 5 * Modified by Douglas Warner
6 * Last Modified: 05/02/2002 6 * Last Modified: 05/02/2002
7 * 7 *
8 * This is a sample mini embedded Perl interpreter (hacked out checks.c and 8 * This is a sample mini embedded Perl interpreter (hacked out checks.c and
9 * perlembed) for use in testing Perl plugins. 9 * perlembed) for use in testing Perl plugins.
10 * 10 *
11 * It can be compiled with the following command (see 'man perlembed' for 11 * It can be compiled with the following command (see 'man perlembed' for
12 * more info): 12 * more info):
13 * 13 *
14 * gcc -omini_epn mini_epn.c `perl -MExtUtils::Embed -e ccopts -e ldopts` 14 * gcc -omini_epn mini_epn.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
@@ -21,7 +21,6 @@
21 * 21 *
22 */ 22 */
23 23
24
25#include <EXTERN.h> 24#include <EXTERN.h>
26#include <perl.h> 25#include <perl.h>
27#include <fcntl.h> 26#include <fcntl.h>
@@ -30,7 +29,7 @@
30/* include PERL xs_init code for module and C library support */ 29/* include PERL xs_init code for module and C library support */
31 30
32#if defined(__cplusplus) 31#if defined(__cplusplus)
33#define is_cplusplus 32# define is_cplusplus
34#endif 33#endif
35 34
36#ifdef is_cplusplus 35#ifdef is_cplusplus
@@ -42,22 +41,20 @@ extern "C" {
42 41
43#ifdef is_cplusplus 42#ifdef is_cplusplus
44} 43}
45# ifndef EXTERN_C 44# ifndef EXTERN_C
46# define EXTERN_C extern "C" 45# define EXTERN_C extern "C"
47# endif 46# endif
48#else 47#else
49# ifndef EXTERN_C 48# ifndef EXTERN_C
50# define EXTERN_C extern 49# define EXTERN_C extern
51# endif 50# endif
52#endif 51#endif
53
54 52
55EXTERN_C void xs_init _((void)); 53EXTERN_C void xs_init _((void));
56 54
57EXTERN_C void boot_DynaLoader _((CV* cv)); 55EXTERN_C void boot_DynaLoader _((CV * cv));
58 56
59EXTERN_C void xs_init(void) 57EXTERN_C void xs_init(void) {
60{
61 char *file = __FILE__; 58 char *file = __FILE__;
62 dXSUB_SYS; 59 dXSUB_SYS;
63 60
@@ -65,85 +62,80 @@ EXTERN_C void xs_init(void)
65 newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); 62 newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
66} 63}
67 64
68
69static PerlInterpreter *perl = NULL; 65static PerlInterpreter *perl = NULL;
70 66
71 67int main(int argc, char **argv, char **env) {
72int main(int argc, char **argv, char **env) 68 char *embedding[] = {"", "p1.pl"};
73{
74 char *embedding[] = { "", "p1.pl" };
75 char plugin_output[1024]; 69 char plugin_output[1024];
76 char buffer[512]; 70 char buffer[512];
77 char tmpfname[32]; 71 char tmpfname[32];
78 char fname[32]; 72 char fname[32];
79 char *args[] = {"","0", "", "", NULL }; 73 char *args[] = {"", "0", "", "", NULL};
80 FILE *fp; 74 FILE *fp;
81 75
82 const int command_line_size = 160; 76 const int command_line_size = 160;
83 char command_line[command_line_size]; 77 char command_line[command_line_size];
84 char *ap ; 78 char *ap;
85 int exitstatus; 79 int exitstatus;
86 int pclose_result; 80 int pclose_result;
87#ifdef THREADEDPERL 81#ifdef THREADEDPERL
88 dTHX; 82 dTHX;
89#endif 83#endif
90 dSP; 84 dSP;
91 85
92 if ((perl=perl_alloc())==NULL) { 86 if ((perl = perl_alloc()) == NULL) {
93 snprintf(buffer,sizeof(buffer),"Error: Could not allocate memory for embedded Perl interpreter!\n"); 87 snprintf(buffer, sizeof(buffer),
94 buffer[sizeof(buffer)-1]='\x0'; 88 "Error: Could not allocate memory for embedded Perl interpreter!\n");
89 buffer[sizeof(buffer) - 1] = '\x0';
95 printf("%s\n", buffer); 90 printf("%s\n", buffer);
96 exit(1); 91 exit(1);
97 } 92 }
98 perl_construct(perl); 93 perl_construct(perl);
99 exitstatus=perl_parse(perl,xs_init,2,embedding,NULL); 94 exitstatus = perl_parse(perl, xs_init, 2, embedding, NULL);
100 if (!exitstatus) { 95 if (!exitstatus) {
101 96
102 exitstatus=perl_run(perl); 97 exitstatus = perl_run(perl);
103 98
104 while(printf("Enter file name: ") && fgets(command_line, command_line_size, stdin)) { 99 while (printf("Enter file name: ") && fgets(command_line, command_line_size, stdin)) {
105 100
106 /* call the subroutine, passing it the filename as an argument */ 101 /* call the subroutine, passing it the filename as an argument */
107 102
108 command_line[strlen(command_line) -1] = '\0'; 103 command_line[strlen(command_line) - 1] = '\0';
109 104
110 strncpy(fname,command_line,strcspn(command_line," ")); 105 strncpy(fname, command_line, strcspn(command_line, " "));
111 fname[strcspn(command_line," ")] = '\x0'; 106 fname[strcspn(command_line, " ")] = '\x0';
112 args[0] = fname ; 107 args[0] = fname;
113 args[3] = command_line + strlen(fname) + 1 ; 108 args[3] = command_line + strlen(fname) + 1;
114 109
115 /* generate a temporary filename to which stdout can be redirected. */ 110 /* generate a temporary filename to which stdout can be redirected. */
116 sprintf(tmpfname,"/tmp/embedded%d",getpid()); 111 sprintf(tmpfname, "/tmp/embedded%d", getpid());
117 args[2] = tmpfname; 112 args[2] = tmpfname;
118 113
119 /* call our perl interpreter to compile and optionally cache the command */ 114 /* call our perl interpreter to compile and optionally cache the command */
120 perl_call_argv("Embed::Persistent::eval_file", G_DISCARD | G_EVAL, args); 115 perl_call_argv("Embed::Persistent::eval_file", G_DISCARD | G_EVAL, args);
121 116
122 perl_call_argv("Embed::Persistent::run_package", G_DISCARD | G_EVAL, args); 117 perl_call_argv("Embed::Persistent::run_package", G_DISCARD | G_EVAL, args);
123 118
124 /* check return status */ 119 /* check return status */
125 if(SvTRUE(ERRSV)){ 120 if (SvTRUE(ERRSV)) {
126 pclose_result=-2; 121 pclose_result = -2;
127 printf("embedded perl ran %s with error %s\n",fname,SvPV(ERRSV,PL_na)); 122 printf("embedded perl ran %s with error %s\n", fname, SvPV(ERRSV, PL_na));
128 } 123 }
129 124
130 /* read back stdout from script */ 125 /* read back stdout from script */
131 fp=fopen(tmpfname, "r"); 126 fp = fopen(tmpfname, "r");
132 127
133 /* default return string in case nothing was returned */ 128 /* default return string in case nothing was returned */
134 strcpy(plugin_output,"(No output!)"); 129 strcpy(plugin_output, "(No output!)");
135
136 fgets(plugin_output,sizeof(plugin_output)-1,fp);
137 plugin_output[sizeof(plugin_output)-1]='\x0';
138 fclose(fp);
139 unlink(tmpfname);
140 printf("embedded perl plugin output was %d,%s\n",pclose_result, plugin_output);
141 130
131 fgets(plugin_output, sizeof(plugin_output) - 1, fp);
132 plugin_output[sizeof(plugin_output) - 1] = '\x0';
133 fclose(fp);
134 unlink(tmpfname);
135 printf("embedded perl plugin output was %d,%s\n", pclose_result, plugin_output);
142 } 136 }
143
144 } 137 }
145 138
146
147 PL_perl_destruct_level = 0; 139 PL_perl_destruct_level = 0;
148 perl_destruct(perl); 140 perl_destruct(perl);
149 perl_free(perl); 141 perl_free(perl);
diff --git a/tools/opttest.pl b/tools/opttest.pl
new file mode 100755
index 00000000..98213082
--- /dev/null
+++ b/tools/opttest.pl
@@ -0,0 +1,74 @@
1#!/usr/bin/perl -w
2use strict;
3use warnings;
4use Test;
5
6# This script (when executed from the monitoring plugins top level directory)
7# executes all the plugins with -h, --help, -V and --version to verify that
8# all of them exit properly with the state UNKNOWN (3)
9
10use vars qw($dir $file $prog $idx $state $output %progs @dirs);
11
12my $tests = 0;
13
14@dirs = qw(plugins plugins-scripts);
15
16foreach my $dir (@dirs) {
17 opendir(DIR, $dir) || die "can't opendir $dir: $!";
18 while ($file = readdir(DIR)) {
19 if (-x "$dir/$file" && -f "$dir/$file") {
20 $tests++;
21 $progs{"$dir/$file"} = $file;
22 }
23 }
24 closedir DIR;
25}
26
27plan tests => $tests;
28
29for my $prog (keys %progs) {
30 $state = 0;
31 $file = `basename $prog`;
32
33 $idx = 1;
34 $output = `$prog -h 2>&1`;
35 if(($? >> 8) != 3) {
36 $state++;
37 print "$prog failed test $idx (help exit code (short form))\n";
38 exit(1);
39 }
40
41 unless ($output =~ m/$progs{$prog}/ms) {
42 $idx++;
43 $state++;
44 print "$output\n$prog failed test $idx\n";
45 }
46
47 $idx++;
48 `$prog --help 2>&1 > /dev/null`;
49 if(($? >> 8) != 3) {
50 $state++;
51 print "$prog failed test $idx (help exit code (long form))\n";
52 exit(1);
53 }
54
55 $idx++;
56 `$prog -V 2>&1 > /dev/null`;
57 if(($? >> 8) != 3) {
58 $state++;
59 print "$prog failed test $idx (version exit code (short form))\n";
60 exit(1);
61 }
62
63 $idx++;
64 `$prog --version 2>&1 > /dev/null`;
65 if(($? >> 8) != 3) {
66 $state++;
67 print "$prog failed test $idx (version exit code (long form))\n";
68 exit(1);
69 }
70
71 print "$prog ($idx tests) ";
72 ok $state,0;
73}
74
diff --git a/tools/tinderbox_build b/tools/tinderbox_build
deleted file mode 100755
index 1a41f577..00000000
--- a/tools/tinderbox_build
+++ /dev/null
@@ -1,290 +0,0 @@
1#!/usr/bin/perl
2# tinderbox_build.pl
3# This script builds the monitoringplugins and then sends
4# logs back to the master tinderbox server
5#
6# This script is based on mozilla-unix.pl which comes with tinderbox2
7#
8# See http://tinderbox.altinity.org for more details
9
10require 5.000;
11
12use strict;
13use Sys::Hostname;
14use Cwd;
15use Time::Local;
16
17my $Version = `git describe --abbrev=4 HEAD`;
18
19my $myhost = hostname;
20chomp($myhost);
21my ($host, $junk) = split(/\./, $myhost);
22
23my $BuildAdministrator = $ENV{TINDERBOX_BUILD_ADMIN} || "$ENV{'USER'}\@$myhost";
24my $TmpDir = $ENV{TMPDIR} || "/tmp";
25
26#Default values of cmdline opts
27my $ReportStatus = 0; # Do not send results to server
28
29# Set these to what makes sense for your system
30
31# Set these proper values for your tinderbox server
32# Have the StrictHostKeyChecking=no so that a new host will automatically add hostkey without
33# prompting. If host key changes, then will get error, so this should still be secure
34my $Tinderbox_server = '-p 1022 -o StrictHostKeyChecking=no tinderbox2@tinderbox.opsera.com';
35
36# These shouldn't really need to be changed
37my $BuildTree = 'monitoringplug';
38my $BuildName = '';
39my $ConfigureArgs = $ENV{CONFIGURE_ARGS};
40
41my $OS = `uname -s`;
42my $OSVer = `uname -r`;
43
44chop($OS, $OSVer);
45
46if ( $OS eq 'AIX' ) {
47 $OSVer = `uname -v`;
48 chop($OSVer);
49 $OSVer = $OSVer . "." . `uname -r`;
50 chop($OSVer);
51}
52
53if ( $OS eq 'IRIX64' ) {
54 $OS = 'IRIX';
55}
56
57if ( $OS eq 'SCO_SV' ) {
58 $OS = 'SCOOS';
59 $OSVer = '5.0';
60}
61
62if ( "$host" ne "" ) {
63 $BuildName = $host . ' ';
64}
65$BuildName .= $OS . ' ' . $OSVer;
66$_ = $BuildName;
67s/ /_/g;
68
69my $logfile = "$_.log";
70
71sub BuildIt {
72 my ($fe, @felist, $EarlyExit, $LastTime);
73
74 my $StartDir = getcwd();
75 $LastTime = 0;
76
77 print "Starting dir is : $StartDir\n";
78
79 my $EarlyExit = 0;
80
81 chdir("$StartDir");
82
83 my $StartTime = time;
84 if (-e (my $file = "monitoring-plugins.spec")) {
85 open F, $file;
86 while (<F>) {
87 if (/^Version: trunk-(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
88 $StartTime = timegm(0, $5, $4, $3, ($2 - 1), ($1 - 1900));
89 last;
90 }
91 }
92 }
93
94 print "Start time is $StartTime",$/;
95
96 my $CurrentDir = getcwd();
97 if ( $CurrentDir ne $StartDir ) {
98 print "startdir: $StartDir, curdir $CurrentDir\n";
99 die "curdir != startdir";
100 }
101
102 unlink( "$logfile" );
103
104 print "opening $logfile\n";
105 open( LOG, ">$logfile" ) || print "can't open $?\n";
106 print LOG "current dir is -- $host:$CurrentDir\n";
107 print LOG "Build Administrator is $BuildAdministrator\n";
108 &PrintEnv;
109
110 my $BuildStatus;
111 if (&configure) {
112 if (&make) {
113 if (&maketest) {
114 $BuildStatus = "success";
115 } else {
116 $BuildStatus = "test_failed";
117 }
118 } else {
119 $BuildStatus = "build_failed";
120 }
121 } else {
122 $BuildStatus = "busted";
123 }
124
125 print LOG "\nBuild Status = $BuildStatus\n";
126
127 close(LOG);
128 chdir("$StartDir");
129
130# TV: Leaving this in, because process mail program probably has some
131# limitation retained
132
133# this fun line added on 2/5/98. do not remove. Translated to english,
134# that's "take any line longer than 1000 characters, and split it into less
135# than 1000 char lines. If any of the resulting lines is
136# a dot on a line by itself, replace that with a blank line."
137# This is to prevent cases where a <cr>.<cr> occurs in the log file. Sendmail
138# interprets that as the end of the mail, and truncates the log before
139# it gets to Tinderbox. (terry weismann, chris yeh)
140#
141# This was replaced by a perl 'port' of the above, written by
142# preed@netscape.com; good things: no need for system() call, and now it's
143# all in perl, so we don't have to do OS checking like before.
144
145 open(LOG, "$logfile") || die "Couldn't open logfile: $!\n";
146 open(OUTLOG, ">${logfile}.last") || die "Couldn't open logfile: $!\n";
147
148 print OUTLOG $/;
149 print OUTLOG "tinderbox: tree: $BuildTree\n";
150 print OUTLOG "tinderbox: builddate: $StartTime\n";
151 print OUTLOG "tinderbox: status: $BuildStatus\n";
152 print OUTLOG "tinderbox: build: $BuildName $fe\n";
153 print OUTLOG "tinderbox: errorparser: unix\n";
154 print OUTLOG "tinderbox: buildfamily: unix\n";
155 print OUTLOG "tinderbox: END\n";
156 print OUTLOG $/;
157
158 while (<LOG>) {
159 my $q = 0;
160
161 for (;;) {
162 my $val = $q * 1000;
163 my $Output = substr($_, $val, 1000);
164
165 last if $Output eq undef;
166
167 $Output =~ s/^\.$//g;
168 $Output =~ s/\n//g;
169 print OUTLOG "$Output\n";
170 $q++;
171 } #EndFor
172
173 } #EndWhile
174
175 close(LOG);
176 close(OUTLOG);
177
178 if ($ReportStatus) {
179 system( "ssh $Tinderbox_server tinderbox_receive < ${logfile}.last" )
180 } else {
181 print <<"EOF"
182Not sending logs to http://tinderbox.altinity.org
183If you have SSH keys setup on the tinderbox server, you can manually send
184with 'ssh $Tinderbox_server tinderbox_receive < ${logfile}.last'
185EOF
186 }
187
188 unlink("$logfile");
189 print "Finished building for tinderbox",$/;
190
191} #EndSub-BuildIt
192
193sub ParseArgs {
194 my($i);
195
196 $i = 0;
197 while( $i < @ARGV ) {
198 if ($ARGV[$i] eq '--version' || $ARGV[$i] eq '-v') {
199 die "$0: version $Version\n";
200 } elsif ($ARGV[$i] eq '-y') {
201 $ReportStatus = 1;
202 } else {
203 &PrintUsage;
204 }
205
206 $i++;
207 } #EndWhile
208
209} #EndSub-ParseArgs
210
211sub PrintUsage {
212 die "usage: $0 [-v | --version ] [-t do not send report to tinderbox server]\n";
213}
214
215sub PrintEnv {
216 my ($key);
217 foreach $key (keys %ENV) {
218 print LOG "$key = $ENV{$key}\n";
219 print "$key = $ENV{$key}\n";
220 }
221
222 # Print the NPTest variables
223 if (-e "/var/tmp/NPTest.cache") {
224 open F, "/var/tmp/NPTest.cache";
225 print LOG "NPTest variables:\n";
226 print LOG <F>;
227 close F;
228 }
229
230} #EndSub-PrintEnv
231
232sub SetupPath {
233 my($Path);
234 $Path = $ENV{PATH};
235 print "Path before: $Path\n";
236
237 # Don't alter path if we're building off a repository tree;
238 # SunOS make will be used only for snapshots and releases.
239 if ( $OS eq 'SunOS' && !( -e '.svn' || -e '.git' )) {
240 $ENV{'PATH'} = '/usr/ccs/bin:' . $ENV{'PATH'};
241 }
242
243 $Path = $ENV{PATH};
244 print "Path After: $Path\n";
245} #EndSub-SetupPath
246
247sub configure {
248 # Configure
249 print LOG "./configure --enable-extra-opts --enable-libtap $ConfigureArgs 2>&1\n";
250 open (CONFIGURE, "./configure --enable-extra-opts --enable-libtap $ConfigureArgs 2>&1 |") || die "../configure: $!\n";
251 while (<CONFIGURE>) {
252 print $_;
253 print LOG $_;
254 }
255 close(CONFIGURE);
256 return ! $?;
257}
258
259sub make {
260 # Building
261 print LOG "make 2>&1\n";
262 open( MAKE, "make 2>&1 |");
263 while ( <MAKE> ) {
264 print $_;
265 print LOG $_;
266 }
267 close( MAKE);
268 return ! $?;
269}
270
271sub maketest {
272 # Tests
273 print LOG "LANG=C make test 2>&1 && make install DESTDIR=$TmpDir/tinderbox_build.$$ 2>&1 && make install-strip DESTDIR=$TmpDir/tinderbox_build2.$$ 2>&1\n";
274 open( MAKE, "LANG=C make test 2>&1 && make install DESTDIR=$TmpDir/tinderbox_build.$$ 2>&1 && make install-strip DESTDIR=$TmpDir/tinderbox_build2.$$ 2>&1 |");
275 while ( <MAKE> ) {
276 print $_;
277 print LOG $_;
278 }
279 close( MAKE);
280 my $rc = $?;
281 system("rm -fr $TmpDir/tinderbox_build.$$ $TmpDir/tinderbox_build2.$$");
282 return ! $rc;
283}
284
285# Main function
286&ParseArgs;
287&SetupPath;
288&BuildIt;
289
2901;