summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--.github/monitoring-plugins.spec3
-rw-r--r--.github/os_detect.sh3
-rwxr-xr-x.github/prepare_debian.sh3
-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--.gitignore2
-rw-r--r--configure.ac67
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/output.c112
-rw-r--r--lib/output.h31
-rw-r--r--lib/perfdata.c72
-rw-r--r--lib/perfdata.h6
-rw-r--r--lib/tests/test_utils.c190
-rw-r--r--lib/utils_base.c438
-rw-r--r--lib/utils_base.h22
-rw-r--r--lib/utils_cmd.c2
-rw-r--r--lib/utils_cmd.h2
-rw-r--r--plugins-root/Makefile.am7
-rw-r--r--plugins-root/check_dhcp.c867
-rw-r--r--plugins-root/check_dhcp.d/config.h50
-rw-r--r--plugins-root/check_icmp.c2922
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.c134
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.h68
-rw-r--r--plugins-root/check_icmp.d/config.h115
-rw-r--r--plugins-root/t/check_dhcp.t24
-rw-r--r--plugins-root/t/check_icmp.t58
-rw-r--r--plugins/Makefile.am27
-rw-r--r--plugins/check_fping.c114
-rw-r--r--plugins/check_fping.d/config.h24
-rw-r--r--plugins/check_http.c3581
-rw-r--r--plugins/check_load.c652
-rw-r--r--plugins/check_load.d/config.h30
-rw-r--r--plugins/check_ntp_peer.c6
-rw-r--r--plugins/check_procs.c1135
-rw-r--r--plugins/check_procs.d/config.h75
-rw-r--r--plugins/check_snmp.c1767
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.c934
-rw-r--r--plugins/check_snmp.d/check_snmp_helpers.h71
-rw-r--r--plugins/check_snmp.d/config.h81
-rw-r--r--plugins/check_ssh.c89
-rw-r--r--plugins/check_users.c253
-rw-r--r--plugins/check_users.d/config.h20
-rw-r--r--plugins/check_users.d/users.c167
-rw-r--r--plugins/check_users.d/users.h18
-rw-r--r--plugins/t/check_load.t18
-rw-r--r--plugins/t/check_snmp.t103
-rw-r--r--plugins/t/check_users.t4
-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_snmp.c175
-rwxr-xr-xplugins/tests/test_check_snmp.t6
-rw-r--r--plugins/utils.h2
56 files changed, 8498 insertions, 6357 deletions
diff --git a/.clang-format b/.clang-format
index ca411edd..0ff68114 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,7 +4,7 @@ TabWidth: 4
4AllowShortIfStatementsOnASingleLine: false 4AllowShortIfStatementsOnASingleLine: false
5BreakBeforeBraces: Attach 5BreakBeforeBraces: Attach
6AlignConsecutiveMacros: true 6AlignConsecutiveMacros: true
7ColumnLimit: 140 7ColumnLimit: 100
8IndentPPDirectives: AfterHash 8IndentPPDirectives: AfterHash
9SortIncludes: Never 9SortIncludes: Never
10AllowShortEnumsOnASingleLine: false 10AllowShortEnumsOnASingleLine: false
diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec
index 10799128..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
diff --git a/.github/os_detect.sh b/.github/os_detect.sh
index 47c762d3..3c5956de 100644
--- a/.github/os_detect.sh
+++ b/.github/os_detect.sh
@@ -22,4 +22,7 @@ else
22 return 1 22 return 1
23fi 23fi
24export 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')
25export 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 f7b6cf9f..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 \
@@ -111,6 +112,8 @@ mkdir -p /var/lib/snmp/mib_indexes
111sed -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
112service snmpd start 113service snmpd start
113 114
115sed 's/^mibs ://' -i /etc/snmp/snmp.conf
116
114# start cron, will be used by check_nagios 117# start cron, will be used by check_nagios
115cron 118cron
116 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 8b14f429..f1fe8b9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -197,6 +197,8 @@ plugins/check_disk.d/.dirstamp
197/plugins/check_udp 197/plugins/check_udp
198/plugins/check_ups 198/plugins/check_ups
199/plugins/check_users 199/plugins/check_users
200/plugins/check_users.d/.deps
201/plugins/check_users.d/.dirstamp
200/plugins/check_vsz 202/plugins/check_vsz
201/plugins/config.h 203/plugins/config.h
202/plugins/config.h.in 204/plugins/config.h.in
diff --git a/configure.ac b/configure.ac
index fdc9b699..705183a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
@@ -1470,17 +1470,6 @@ AC_ARG_WITH(snmpgetnext_command,
1470AS_IF([test -n "$PATH_TO_SNMPGET"], [ 1470AS_IF([test -n "$PATH_TO_SNMPGET"], [
1471 AC_DEFINE_UNQUOTED(PATH_TO_SNMPGET,"$PATH_TO_SNMPGET",[path to snmpget binary]) 1471 AC_DEFINE_UNQUOTED(PATH_TO_SNMPGET,"$PATH_TO_SNMPGET",[path to snmpget binary])
1472 EXTRAS="$EXTRAS check_hpjd" 1472 EXTRAS="$EXTRAS check_hpjd"
1473
1474 dnl PATH_TO_SNMPGETNEXT is used unconditionally in check_snmp:
1475 dnl
1476 dnl https://github.com/nagios-plugins/nagios-plugins/issues/788
1477 dnl
1478 AS_IF([test -n "$PATH_TO_SNMPGETNEXT"], [
1479 AC_DEFINE_UNQUOTED(PATH_TO_SNMPGETNEXT,"$PATH_TO_SNMPGETNEXT",[path to snmpgetnext binary])
1480 EXTRAS="$EXTRAS check_snmp\$(EXEEXT)"
1481 ], [
1482 AC_MSG_WARN([Get snmpgetnext from https://net-snmp.sourceforge.io/ to build the check_snmp plugin])
1483 ])
1484], [ 1473], [
1485 AC_MSG_WARN([Get snmpget from https://net-snmp.sourceforge.io/ to build the check_hpjd and check_snmp plugins]) 1474 AC_MSG_WARN([Get snmpget from https://net-snmp.sourceforge.io/ to build the check_hpjd and check_snmp plugins])
1486]) 1475])
@@ -1493,6 +1482,16 @@ else
1493 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])
1494fi 1483fi
1495 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
1496AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat) 1495AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat)
1497AC_PATH_PROG(PATH_TO_QSTAT,qstat) 1496AC_PATH_PROG(PATH_TO_QSTAT,qstat)
1498AC_ARG_WITH(qstat_command, 1497AC_ARG_WITH(qstat_command,
@@ -1519,21 +1518,47 @@ then
1519fi 1518fi
1520 1519
1521AC_PATH_PROG(PATH_TO_FPING,fping) 1520AC_PATH_PROG(PATH_TO_FPING,fping)
1522AC_PATH_PROG(PATH_TO_FPING6,fping6)
1523 1521
1524AC_ARG_WITH(fping_command, 1522AC_ARG_WITH(fping_command,
1525 ACX_HELP_STRING([--with-fping-command=PATH], 1523 ACX_HELP_STRING([--with-fping-command=PATH],
1526 [Path to fping command]), PATH_TO_FPING=$withval) 1524 [Path to fping command]), PATH_TO_FPING=$withval)
1527AC_ARG_WITH(fping6_command, 1525if test -n "$PATH_TO_FPING"; then
1528 ACX_HELP_STRING([--with-fping6-command=PATH],
1529 [Path to fping6 command]), PATH_TO_FPING6=$withval)
1530
1531if test -n "$PATH_TO_FPING"
1532then
1533 AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping]) 1526 AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping])
1534 EXTRAS="$EXTRAS check_fping\$(EXEEXT)" 1527 EXTRAS="$EXTRAS check_fping\$(EXEEXT)"
1535 if test x"$with_ipv6" != xno && test -n "$PATH_TO_FPING6"; then 1528
1536 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
1537 fi 1562 fi
1538else 1563else
1539 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 a9f3ff40..27a08278 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,7 +4,7 @@ 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_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c 10libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c
diff --git a/lib/output.c b/lib/output.c
index c408a2f5..34ff7dab 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -16,7 +16,8 @@ static mp_output_format output_format = MP_FORMAT_DEFAULT;
16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL; 16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL;
17 17
18// == Prototypes == 18// == Prototypes ==
19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation); 19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
20 unsigned int indentation);
20static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck); 21static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
21 22
22// == Implementation == 23// == Implementation ==
@@ -58,7 +59,9 @@ static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
58 * It sets useful defaults 59 * It sets useful defaults
59 */ 60 */
60mp_check mp_check_init(void) { 61mp_check mp_check_init(void) {
61 mp_check check = {0}; 62 mp_check check = {
63 .evaluation_function = &mp_eval_check_default,
64 };
62 return check; 65 return check;
63} 66}
64 67
@@ -121,7 +124,8 @@ void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata
121 */ 124 */
122int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) { 125int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
123 if (subcheck.output == NULL) { 126 if (subcheck.output == NULL) {
124 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL"); 127 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__,
128 "Sub check output is NULL");
125 } 129 }
126 130
127 mp_subcheck_list *tmp = NULL; 131 mp_subcheck_list *tmp = NULL;
@@ -194,18 +198,30 @@ char *get_subcheck_summary(mp_check check) {
194 return result; 198 return result;
195} 199}
196 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
197/* 208/*
198 * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states 209 * Generate the result state of a mp_subcheck object based on its own state and its subchecks
210 * states
199 */ 211 */
200mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) { 212mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) {
201 if (check.state_set_explicitly) { 213 if (subcheck.evaluation_function != NULL) {
202 return check.state; 214 return subcheck.evaluation_function(subcheck);
203 } 215 }
204 216
205 mp_subcheck_list *scl = check.subchecks; 217 if (subcheck.state_set_explicitly) {
218 return subcheck.state;
219 }
220
221 mp_subcheck_list *scl = subcheck.subchecks;
206 222
207 if (scl == NULL) { 223 if (scl == NULL) {
208 return check.default_state; 224 return subcheck.default_state;
209 } 225 }
210 226
211 mp_state_enum result = STATE_OK; 227 mp_state_enum result = STATE_OK;
@@ -218,10 +234,18 @@ mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
218 return result; 234 return result;
219} 235}
220 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
221/* 245/*
222 * Generate the result state of a mp_check object based on it's own state and it's subchecks states 246 * Generate the result state of a mp_check object based on it's own state and it's subchecks states
223 */ 247 */
224mp_state_enum mp_compute_check_state(const mp_check check) { 248mp_state_enum mp_eval_check_default(const mp_check check) {
225 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here 249 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
226 250
227 mp_subcheck_list *scl = check.subchecks; 251 mp_subcheck_list *scl = check.subchecks;
@@ -253,8 +277,10 @@ char *mp_fmt_output(mp_check check) {
253 mp_subcheck_list *subchecks = check.subchecks; 277 mp_subcheck_list *subchecks = check.subchecks;
254 278
255 while (subchecks != NULL) { 279 while (subchecks != NULL) {
256 if (level_of_detail == MP_DETAIL_ALL || mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) { 280 if (level_of_detail == MP_DETAIL_ALL ||
257 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); 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));
258 } 284 }
259 subchecks = subchecks->next; 285 subchecks = subchecks->next;
260 } 286 }
@@ -266,7 +292,8 @@ char *mp_fmt_output(mp_check check) {
266 if (pd_string == NULL) { 292 if (pd_string == NULL) {
267 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); 293 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
268 } else { 294 } else {
269 asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); 295 asprintf(&pd_string, "%s %s", pd_string,
296 fmt_subcheck_perfdata(subchecks->subcheck));
270 } 297 }
271 298
272 subchecks = subchecks->next; 299 subchecks = subchecks->next;
@@ -335,22 +362,55 @@ static char *generate_indentation_string(unsigned int indentation) {
335/* 362/*
336 * Helper function to generate the output string of mp_subcheck 363 * Helper function to generate the output string of mp_subcheck
337 */ 364 */
338static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) { 365static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
366 unsigned int indentation) {
339 char *result = NULL; 367 char *result = NULL;
340 mp_subcheck_list *subchecks = NULL; 368 mp_subcheck_list *subchecks = NULL;
341 369
342 switch (output_format) { 370 switch (output_format) {
343 case MP_FORMAT_MULTI_LINE: 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, generate_indentation_string(indentation+1)); // one more indentation to make it look better
381
382 if (*(tmp_string + 1) != '\0') {
383 check.output = tmp_string + 1;
384 have_residual_chars = true;
385 } else {
386 // Null after the \n, so this is the end
387 have_residual_chars = false;
388 break;
389 }
390
391 tmp_string = strchr(check.output, '\n');
392 }
393
394 // add the rest (if any)
395 if (have_residual_chars) {
396 char *tmp = check.output;
397 xasprintf(&check.output, "%s\n%s%s", intermediate_string, generate_indentation_string(indentation+1), tmp);
398 } else {
399 check.output = intermediate_string;
400 }
401 }
344 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), 402 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)),
345 check.output); 403 check.output);
346 404
347 subchecks = check.subchecks; 405 subchecks = check.subchecks;
348 406
349 while (subchecks != NULL) { 407 while (subchecks != NULL) {
350 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); 408 asprintf(&result, "%s\n%s", result,
409 fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
351 subchecks = subchecks->next; 410 subchecks = subchecks->next;
352 } 411 }
353 return result; 412 return result;
413 }
354 default: 414 default:
355 die(STATE_UNKNOWN, "Invalid format"); 415 die(STATE_UNKNOWN, "Invalid format");
356 } 416 }
@@ -551,3 +611,23 @@ mp_output_format mp_get_format(void) { return output_format; }
551void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; } 611void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; }
552 612
553mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; } 613mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; }
614
615mp_state_enum mp_eval_ok(mp_check overall) {
616 (void)overall;
617 return STATE_OK;
618}
619
620mp_state_enum mp_eval_warning(mp_check overall) {
621 (void)overall;
622 return STATE_WARNING;
623}
624
625mp_state_enum mp_eval_critical(mp_check overall) {
626 (void)overall;
627 return STATE_CRITICAL;
628}
629
630mp_state_enum mp_eval_unknown(mp_check overall) {
631 (void)overall;
632 return STATE_UNKNOWN;
633}
diff --git a/lib/output.h b/lib/output.h
index 3bd91f90..c63c8e3f 100644
--- a/lib/output.h
+++ b/lib/output.h
@@ -7,15 +7,21 @@
7/* 7/*
8 * A partial check result 8 * A partial check result
9 */ 9 */
10typedef struct { 10typedef struct mp_subcheck mp_subcheck;
11struct mp_subcheck {
11 mp_state_enum state; // OK, Warning, Critical ... set explicitly 12 mp_state_enum state; // OK, Warning, Critical ... set explicitly
12 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly 13 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
13 bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks) 14 bool state_set_explicitly; // was the state set explicitly (or should it be derived from
15 // subchecks)
14 16
15 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..") 17 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP
16 pd_list *perfdata; // Performance data for this check 18 // connection to..")
19 pd_list *perfdata; // Performance data for this check
17 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy 20 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
18} mp_subcheck; 21
22 // the evaluation_functions computes the state of subcheck
23 mp_state_enum (*evaluation_function)(mp_subcheck);
24};
19 25
20/* 26/*
21 * A list of subchecks, used in subchecks and the main check 27 * A list of subchecks, used in subchecks and the main check
@@ -57,10 +63,14 @@ mp_output_detail_level mp_get_level_of_detail(void);
57 * The final result is always derived from the children and the "worst" state 63 * The final result is always derived from the children and the "worst" state
58 * in the first layer of subchecks 64 * in the first layer of subchecks
59 */ 65 */
60typedef struct { 66typedef struct mp_check mp_check;
67struct mp_check {
61 char *summary; // Overall summary, if not set a summary will be automatically generated 68 char *summary; // Overall summary, if not set a summary will be automatically generated
62 mp_subcheck_list *subchecks; 69 mp_subcheck_list *subchecks;
63} mp_check; 70
71 // the evaluation_functions computes the state of check
72 mp_state_enum (*evaluation_function)(mp_check);
73};
64 74
65mp_check mp_check_init(void); 75mp_check mp_check_init(void);
66mp_subcheck mp_subcheck_init(void); 76mp_subcheck mp_subcheck_init(void);
@@ -78,6 +88,13 @@ void mp_add_summary(mp_check check[static 1], char *summary);
78mp_state_enum mp_compute_check_state(mp_check); 88mp_state_enum mp_compute_check_state(mp_check);
79mp_state_enum mp_compute_subcheck_state(mp_subcheck); 89mp_state_enum mp_compute_subcheck_state(mp_subcheck);
80 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
81typedef struct { 98typedef struct {
82 bool parsing_success; 99 bool parsing_success;
83 mp_output_format output_format; 100 mp_output_format output_format;
diff --git a/lib/perfdata.c b/lib/perfdata.c
index f425ffcf..2930a8bc 100644
--- a/lib/perfdata.c
+++ b/lib/perfdata.c
@@ -33,7 +33,18 @@ char *pd_value_to_string(const mp_perfdata_value pd) {
33char *pd_to_string(mp_perfdata pd) { 33char *pd_to_string(mp_perfdata pd) {
34 assert(pd.label != NULL); 34 assert(pd.label != NULL);
35 char *result = NULL; 35 char *result = NULL;
36 asprintf(&result, "'%s'=", pd.label); 36
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 }
37 48
38 asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); 49 asprintf(&result, "%s%s", result, pd_value_to_string(pd.value));
39 50
@@ -249,7 +260,9 @@ char *mp_range_to_string(const mp_range input) {
249 return result; 260 return result;
250} 261}
251 262
252mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); } 263mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) {
264 return mp_set_pd_value_double(pd, value);
265}
253 266
254mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { 267mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
255 pd.value.pd_double = value; 268 pd.value.pd_double = value;
@@ -257,11 +270,25 @@ mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
257 return pd; 270 return pd;
258} 271}
259 272
260mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } 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}
261 276
262mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } 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}
263 284
264mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); } 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}
265 292
266mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) { 293mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) {
267 return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); 294 return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
@@ -286,15 +313,33 @@ mp_perfdata_value mp_create_pd_value_double(double value) {
286 return res; 313 return res;
287} 314}
288 315
289mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } 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}
290 323
291mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } 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}
292 327
293mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } 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}
294 335
295mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); } 336mp_perfdata_value mp_create_pd_value_long(long value) {
337 return mp_create_pd_value_long_long((long long)value);
338}
296 339
297mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } 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}
298 343
299mp_perfdata_value mp_create_pd_value_long_long(long long value) { 344mp_perfdata_value mp_create_pd_value_long_long(long long value) {
300 mp_perfdata_value res = {0}; 345 mp_perfdata_value res = {0};
@@ -360,6 +405,13 @@ mp_range_parsed mp_parse_range_string(const char *input) {
360 } 405 }
361 406
362 char *working_copy = strdup(input); 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 }
363 input = working_copy; 415 input = working_copy;
364 416
365 char *separator = index(working_copy, ':'); 417 char *separator = index(working_copy, ':');
diff --git a/lib/perfdata.h b/lib/perfdata.h
index cb552678..c5d4a61d 100644
--- a/lib/perfdata.h
+++ b/lib/perfdata.h
@@ -45,7 +45,7 @@ typedef struct range_struct {
45 double start; 45 double start;
46 bool start_infinity; 46 bool start_infinity;
47 double end; 47 double end;
48 int end_infinity; 48 bool end_infinity;
49 int alert_on; /* OUTSIDE (default) or INSIDE */ 49 int alert_on; /* OUTSIDE (default) or INSIDE */
50 char *text; /* original unparsed text input */ 50 char *text; /* original unparsed text input */
51} range; 51} range;
@@ -155,6 +155,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
155 _Generic((V), \ 155 _Generic((V), \
156 float: mp_create_pd_value_float, \ 156 float: mp_create_pd_value_float, \
157 double: mp_create_pd_value_double, \ 157 double: mp_create_pd_value_double, \
158 char: mp_create_pd_value_char, \
159 unsigned char: mp_create_pd_value_u_char, \
158 int: mp_create_pd_value_int, \ 160 int: mp_create_pd_value_int, \
159 unsigned int: mp_create_pd_value_u_int, \ 161 unsigned int: mp_create_pd_value_u_int, \
160 long: mp_create_pd_value_long, \ 162 long: mp_create_pd_value_long, \
@@ -164,6 +166,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
164 166
165mp_perfdata_value mp_create_pd_value_float(float); 167mp_perfdata_value mp_create_pd_value_float(float);
166mp_perfdata_value mp_create_pd_value_double(double); 168mp_perfdata_value mp_create_pd_value_double(double);
169mp_perfdata_value mp_create_pd_value_char(char);
170mp_perfdata_value mp_create_pd_value_u_char(unsigned char);
167mp_perfdata_value mp_create_pd_value_int(int); 171mp_perfdata_value mp_create_pd_value_int(int);
168mp_perfdata_value mp_create_pd_value_u_int(unsigned int); 172mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
169mp_perfdata_value mp_create_pd_value_long(long); 173mp_perfdata_value mp_create_pd_value_long(long);
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/utils_base.c b/lib/utils_base.c
index c49a473f..29b393d0 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -33,12 +33,12 @@
33#include <unistd.h> 33#include <unistd.h>
34#include <sys/types.h> 34#include <sys/types.h>
35 35
36#define np_free(ptr) \ 36#define np_free(ptr) \
37 { \ 37 { \
38 if (ptr) { \ 38 if (ptr) { \
39 free(ptr); \ 39 free(ptr); \
40 ptr = NULL; \ 40 ptr = NULL; \
41 } \ 41 } \
42 } 42 }
43 43
44monitoring_plugin *this_monitoring_plugin = NULL; 44monitoring_plugin *this_monitoring_plugin = NULL;
@@ -46,7 +46,7 @@ monitoring_plugin *this_monitoring_plugin = NULL;
46int timeout_state = STATE_CRITICAL; 46int timeout_state = STATE_CRITICAL;
47unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; 47unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
48 48
49bool _np_state_read_file(FILE *); 49bool _np_state_read_file(FILE *state_file);
50 50
51void np_init(char *plugin_name, int argc, char **argv) { 51void np_init(char *plugin_name, int argc, char **argv) {
52 if (this_monitoring_plugin == NULL) { 52 if (this_monitoring_plugin == NULL) {
@@ -74,14 +74,6 @@ void np_set_args(int argc, char **argv) {
74 74
75void np_cleanup(void) { 75void np_cleanup(void) {
76 if (this_monitoring_plugin != NULL) { 76 if (this_monitoring_plugin != NULL) {
77 if (this_monitoring_plugin->state != NULL) {
78 if (this_monitoring_plugin->state->state_data) {
79 np_free(this_monitoring_plugin->state->state_data->data);
80 np_free(this_monitoring_plugin->state->state_data);
81 }
82 np_free(this_monitoring_plugin->state->name);
83 np_free(this_monitoring_plugin->state);
84 }
85 np_free(this_monitoring_plugin->plugin_name); 77 np_free(this_monitoring_plugin->plugin_name);
86 np_free(this_monitoring_plugin); 78 np_free(this_monitoring_plugin);
87 } 79 }
@@ -153,7 +145,8 @@ range *parse_range_string(char *str) {
153 set_range_end(temp_range, end); 145 set_range_end(temp_range, end);
154 } 146 }
155 147
156 if (temp_range->start_infinity == true || temp_range->end_infinity == true || temp_range->start <= temp_range->end) { 148 if (temp_range->start_infinity || temp_range->end_infinity ||
149 temp_range->start <= temp_range->end) {
157 return temp_range; 150 return temp_range;
158 } 151 }
159 free(temp_range); 152 free(temp_range);
@@ -205,12 +198,14 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
205 printf("Threshold not set"); 198 printf("Threshold not set");
206 } else { 199 } else {
207 if (my_threshold->warning) { 200 if (my_threshold->warning) {
208 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); 201 printf("Warning: start=%g end=%g; ", my_threshold->warning->start,
202 my_threshold->warning->end);
209 } else { 203 } else {
210 printf("Warning not set; "); 204 printf("Warning not set; ");
211 } 205 }
212 if (my_threshold->critical) { 206 if (my_threshold->critical) {
213 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); 207 printf("Critical: start=%g end=%g", my_threshold->critical->start,
208 my_threshold->critical->end);
214 } else { 209 } else {
215 printf("Critical not set"); 210 printf("Critical not set");
216 } 211 }
@@ -222,15 +217,16 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
222bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { 217bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
223 bool is_inside = false; 218 bool is_inside = false;
224 219
225 if (my_range.end_infinity == false && my_range.start_infinity == false) { 220 if (!my_range.end_infinity && !my_range.start_infinity) {
226 // range: .........|---inside---|........... 221 // range: .........|---inside---|...........
227 // value 222 // value
228 is_inside = ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)); 223 is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) &&
229 } else if (my_range.start_infinity == false && my_range.end_infinity == true) { 224 (cmp_perfdata_value(value, my_range.end) <= 0));
225 } else if (!my_range.start_infinity && my_range.end_infinity) {
230 // range: .........|---inside--------- 226 // range: .........|---inside---------
231 // value 227 // value
232 is_inside = (cmp_perfdata_value(my_range.start, value) < 0); 228 is_inside = (cmp_perfdata_value(value, my_range.start) >= 0);
233 } else if (my_range.start_infinity == true && my_range.end_infinity == false) { 229 } else if (my_range.start_infinity && !my_range.end_infinity) {
234 // range: -inside--------|.................... 230 // range: -inside--------|....................
235 // value 231 // value
236 is_inside = (cmp_perfdata_value(value, my_range.end) == -1); 232 is_inside = (cmp_perfdata_value(value, my_range.end) == -1);
@@ -239,7 +235,8 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
239 is_inside = true; 235 is_inside = true;
240 } 236 }
241 237
242 if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { 238 if ((is_inside && my_range.alert_on_inside_range == INSIDE) ||
239 (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
243 return true; 240 return true;
244 } 241 }
245 242
@@ -256,21 +253,21 @@ bool check_range(double value, range *my_range) {
256 yes = false; 253 yes = false;
257 } 254 }
258 255
259 if (my_range->end_infinity == false && my_range->start_infinity == false) { 256 if (!my_range->end_infinity && !my_range->start_infinity) {
260 if ((my_range->start <= value) && (value <= my_range->end)) { 257 if ((my_range->start <= value) && (value <= my_range->end)) {
261 return no; 258 return no;
262 } 259 }
263 return yes; 260 return yes;
264 } 261 }
265 262
266 if (my_range->start_infinity == false && my_range->end_infinity == true) { 263 if (!my_range->start_infinity && my_range->end_infinity) {
267 if (my_range->start <= value) { 264 if (my_range->start <= value) {
268 return no; 265 return no;
269 } 266 }
270 return yes; 267 return yes;
271 } 268 }
272 269
273 if (my_range->start_infinity == true && my_range->end_infinity == false) { 270 if (my_range->start_infinity && !my_range->end_infinity) {
274 if (value <= my_range->end) { 271 if (value <= my_range->end) {
275 return no; 272 return no;
276 } 273 }
@@ -282,12 +279,12 @@ bool check_range(double value, range *my_range) {
282/* Returns status */ 279/* Returns status */
283int get_status(double value, thresholds *my_thresholds) { 280int get_status(double value, thresholds *my_thresholds) {
284 if (my_thresholds->critical != NULL) { 281 if (my_thresholds->critical != NULL) {
285 if (check_range(value, my_thresholds->critical) == true) { 282 if (check_range(value, my_thresholds->critical)) {
286 return STATE_CRITICAL; 283 return STATE_CRITICAL;
287 } 284 }
288 } 285 }
289 if (my_thresholds->warning != NULL) { 286 if (my_thresholds->warning != NULL) {
290 if (check_range(value, my_thresholds->warning) == true) { 287 if (check_range(value, my_thresholds->warning)) {
291 return STATE_WARNING; 288 return STATE_WARNING;
292 } 289 }
293 } 290 }
@@ -296,32 +293,31 @@ int get_status(double value, thresholds *my_thresholds) {
296 293
297char *np_escaped_string(const char *string) { 294char *np_escaped_string(const char *string) {
298 char *data; 295 char *data;
299 int i; 296 int write_index = 0;
300 int j = 0;
301 data = strdup(string); 297 data = strdup(string);
302 for (i = 0; data[i]; i++) { 298 for (int i = 0; data[i]; i++) {
303 if (data[i] == '\\') { 299 if (data[i] == '\\') {
304 switch (data[++i]) { 300 switch (data[++i]) {
305 case 'n': 301 case 'n':
306 data[j++] = '\n'; 302 data[write_index++] = '\n';
307 break; 303 break;
308 case 'r': 304 case 'r':
309 data[j++] = '\r'; 305 data[write_index++] = '\r';
310 break; 306 break;
311 case 't': 307 case 't':
312 data[j++] = '\t'; 308 data[write_index++] = '\t';
313 break; 309 break;
314 case '\\': 310 case '\\':
315 data[j++] = '\\'; 311 data[write_index++] = '\\';
316 break; 312 break;
317 default: 313 default:
318 data[j++] = data[i]; 314 data[write_index++] = data[i];
319 } 315 }
320 } else { 316 } else {
321 data[j++] = data[i]; 317 data[write_index++] = data[i];
322 } 318 }
323 } 319 }
324 data[j] = '\0'; 320 data[write_index] = '\0';
325 return data; 321 return data;
326} 322}
327 323
@@ -336,33 +332,35 @@ int np_check_if_root(void) { return (geteuid() == 0); }
336char *np_extract_value(const char *varlist, const char *name, char sep) { 332char *np_extract_value(const char *varlist, const char *name, char sep) {
337 char *tmp = NULL; 333 char *tmp = NULL;
338 char *value = NULL; 334 char *value = NULL;
339 int i;
340 335
341 while (1) { 336 while (true) {
342 /* Strip any leading space */ 337 /* Strip any leading space */
343 for (; isspace(varlist[0]); varlist++) 338 for (; isspace(varlist[0]); varlist++) {
344 ; 339 ;
340 }
345 341
346 if (strncmp(name, varlist, strlen(name)) == 0) { 342 if (strncmp(name, varlist, strlen(name)) == 0) {
347 varlist += strlen(name); 343 varlist += strlen(name);
348 /* strip trailing spaces */ 344 /* strip trailing spaces */
349 for (; isspace(varlist[0]); varlist++) 345 for (; isspace(varlist[0]); varlist++) {
350 ; 346 ;
347 }
351 348
352 if (varlist[0] == '=') { 349 if (varlist[0] == '=') {
353 /* We matched the key, go past the = sign */ 350 /* We matched the key, go past the = sign */
354 varlist++; 351 varlist++;
355 /* strip leading spaces */ 352 /* strip leading spaces */
356 for (; isspace(varlist[0]); varlist++) 353 for (; isspace(varlist[0]); varlist++) {
357 ; 354 ;
355 }
358 356
359 if ((tmp = index(varlist, sep))) { 357 if ((tmp = index(varlist, sep))) {
360 /* Value is delimited by a comma */ 358 /* Value is delimited by a comma */
361 if (tmp - varlist == 0) { 359 if (tmp - varlist == 0) {
362 continue; 360 continue;
363 } 361 }
364 value = (char *)calloc(1, tmp - varlist + 1); 362 value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1));
365 strncpy(value, varlist, tmp - varlist); 363 strncpy(value, varlist, (unsigned long)(tmp - varlist));
366 value[tmp - varlist] = '\0'; 364 value[tmp - varlist] = '\0';
367 } else { 365 } else {
368 /* Value is delimited by a \0 */ 366 /* Value is delimited by a \0 */
@@ -387,7 +385,7 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
387 385
388 /* Clean-up trailing spaces/newlines */ 386 /* Clean-up trailing spaces/newlines */
389 if (value) { 387 if (value) {
390 for (i = strlen(value) - 1; isspace(value[i]); i--) { 388 for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) {
391 value[i] = '\0'; 389 value[i] = '\0';
392 } 390 }
393 } 391 }
@@ -429,349 +427,3 @@ int mp_translate_state(char *state_text) {
429 } 427 }
430 return ERROR; 428 return ERROR;
431} 429}
432
433/*
434 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
435 * hopefully a unique key per service/plugin invocation. Use the extra-opts
436 * parse of argv, so that uniqueness in parameters are reflected there.
437 */
438char *_np_state_generate_key(void) {
439 int i;
440 char **argv = this_monitoring_plugin->argv;
441 char keyname[41];
442 char *p = NULL;
443
444 unsigned char result[256];
445
446#ifdef USE_OPENSSL
447 /*
448 * This code path is chosen if openssl is available (which should be the most common
449 * scenario). Alternatively, the gnulib implementation/
450 *
451 */
452 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
453
454 EVP_DigestInit(ctx, EVP_sha256());
455
456 for (i = 0; i < this_monitoring_plugin->argc; i++) {
457 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
458 }
459
460 EVP_DigestFinal(ctx, result, NULL);
461#else
462
463 struct sha256_ctx ctx;
464
465 for (i = 0; i < this_monitoring_plugin->argc; i++) {
466 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
467 }
468
469 sha256_finish_ctx(&ctx, result);
470#endif // FOUNDOPENSSL
471
472 for (i = 0; i < 20; ++i) {
473 sprintf(&keyname[2 * i], "%02x", result[i]);
474 }
475
476 keyname[40] = '\0';
477
478 p = strdup(keyname);
479 if (p == NULL) {
480 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
481 }
482 return p;
483}
484
485void _cleanup_state_data(void) {
486 if (this_monitoring_plugin->state->state_data != NULL) {
487 np_free(this_monitoring_plugin->state->state_data->data);
488 np_free(this_monitoring_plugin->state->state_data);
489 }
490}
491
492/*
493 * Internal function. Returns either:
494 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
495 * statically compiled shared state directory
496 */
497char *_np_state_calculate_location_prefix(void) {
498 char *env_dir;
499
500 /* Do not allow passing MP_STATE_PATH in setuid plugins
501 * for security reasons */
502 if (!mp_suid()) {
503 env_dir = getenv("MP_STATE_PATH");
504 if (env_dir && env_dir[0] != '\0') {
505 return env_dir;
506 }
507 /* This is the former ENV, for backward-compatibility */
508 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
509 if (env_dir && env_dir[0] != '\0') {
510 return env_dir;
511 }
512 }
513
514 return NP_STATE_DIR_PREFIX;
515}
516
517/*
518 * Initiatializer for state routines.
519 * Sets variables. Generates filename. Returns np_state_key. die with
520 * UNKNOWN if exception
521 */
522void np_enable_state(char *keyname, int expected_data_version) {
523 state_key *this_state = NULL;
524 char *temp_filename = NULL;
525 char *temp_keyname = NULL;
526 char *p = NULL;
527 int ret;
528
529 if (this_monitoring_plugin == NULL) {
530 die(STATE_UNKNOWN, _("This requires np_init to be called"));
531 }
532
533 this_state = (state_key *)calloc(1, sizeof(state_key));
534 if (this_state == NULL) {
535 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
536 }
537
538 if (keyname == NULL) {
539 temp_keyname = _np_state_generate_key();
540 } else {
541 temp_keyname = strdup(keyname);
542 if (temp_keyname == NULL) {
543 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
544 }
545 }
546 /* Die if invalid characters used for keyname */
547 p = temp_keyname;
548 while (*p != '\0') {
549 if (!(isalnum(*p) || *p == '_')) {
550 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
551 }
552 p++;
553 }
554 this_state->name = temp_keyname;
555 this_state->plugin_name = this_monitoring_plugin->plugin_name;
556 this_state->data_version = expected_data_version;
557 this_state->state_data = NULL;
558
559 /* Calculate filename */
560 ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
561 this_monitoring_plugin->plugin_name, this_state->name);
562 if (ret < 0) {
563 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
564 }
565
566 this_state->_filename = temp_filename;
567
568 this_monitoring_plugin->state = this_state;
569}
570
571/*
572 * Will return NULL if no data is available (first run). If key currently
573 * exists, read data. If state file format version is not expected, return
574 * as if no data. Get state data version number and compares to expected.
575 * If numerically lower, then return as no previous state. die with UNKNOWN
576 * if exceptional error.
577 */
578state_data *np_state_read(void) {
579 state_data *this_state_data = NULL;
580 FILE *statefile;
581 bool rc = false;
582
583 if (this_monitoring_plugin == NULL) {
584 die(STATE_UNKNOWN, _("This requires np_init to be called"));
585 }
586
587 /* Open file. If this fails, no previous state found */
588 statefile = fopen(this_monitoring_plugin->state->_filename, "r");
589 if (statefile != NULL) {
590
591 this_state_data = (state_data *)calloc(1, sizeof(state_data));
592 if (this_state_data == NULL) {
593 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
594 }
595
596 this_state_data->data = NULL;
597 this_monitoring_plugin->state->state_data = this_state_data;
598
599 rc = _np_state_read_file(statefile);
600
601 fclose(statefile);
602 }
603
604 if (!rc) {
605 _cleanup_state_data();
606 }
607
608 return this_monitoring_plugin->state->state_data;
609}
610
611/*
612 * Read the state file
613 */
614bool _np_state_read_file(FILE *f) {
615 bool status = false;
616 size_t pos;
617 char *line;
618 int i;
619 int failure = 0;
620 time_t current_time, data_time;
621 enum {
622 STATE_FILE_VERSION,
623 STATE_DATA_VERSION,
624 STATE_DATA_TIME,
625 STATE_DATA_TEXT,
626 STATE_DATA_END
627 } expected = STATE_FILE_VERSION;
628
629 time(&current_time);
630
631 /* Note: This introduces a limit of 1024 bytes in the string data */
632 line = (char *)calloc(1, 1024);
633 if (line == NULL) {
634 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
635 }
636
637 while (!failure && (fgets(line, 1024, f)) != NULL) {
638 pos = strlen(line);
639 if (line[pos - 1] == '\n') {
640 line[pos - 1] = '\0';
641 }
642
643 if (line[0] == '#') {
644 continue;
645 }
646
647 switch (expected) {
648 case STATE_FILE_VERSION:
649 i = atoi(line);
650 if (i != NP_STATE_FORMAT_VERSION) {
651 failure++;
652 } else {
653 expected = STATE_DATA_VERSION;
654 }
655 break;
656 case STATE_DATA_VERSION:
657 i = atoi(line);
658 if (i != this_monitoring_plugin->state->data_version) {
659 failure++;
660 } else {
661 expected = STATE_DATA_TIME;
662 }
663 break;
664 case STATE_DATA_TIME:
665 /* If time > now, error */
666 data_time = strtoul(line, NULL, 10);
667 if (data_time > current_time) {
668 failure++;
669 } else {
670 this_monitoring_plugin->state->state_data->time = data_time;
671 expected = STATE_DATA_TEXT;
672 }
673 break;
674 case STATE_DATA_TEXT:
675 this_monitoring_plugin->state->state_data->data = strdup(line);
676 if (this_monitoring_plugin->state->state_data->data == NULL) {
677 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
678 }
679 expected = STATE_DATA_END;
680 status = true;
681 break;
682 case STATE_DATA_END:;
683 }
684 }
685
686 np_free(line);
687 return status;
688}
689
690/*
691 * If time=NULL, use current time. Create state file, with state format
692 * version, default text. Writes version, time, and data. Avoid locking
693 * problems - use mv to write and then swap. Possible loss of state data if
694 * two things writing to same key at same time.
695 * Will die with UNKNOWN if errors
696 */
697void np_state_write_string(time_t data_time, char *data_string) {
698 FILE *fp;
699 char *temp_file = NULL;
700 int fd = 0, result = 0;
701 time_t current_time;
702 char *directories = NULL;
703 char *p = NULL;
704
705 if (data_time == 0) {
706 time(&current_time);
707 } else {
708 current_time = data_time;
709 }
710
711 /* If file doesn't currently exist, create directories */
712 if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
713 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
714 if (result < 0) {
715 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
716 }
717
718 for (p = directories + 1; *p; p++) {
719 if (*p == '/') {
720 *p = '\0';
721 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
722 /* Can't free this! Otherwise error message is wrong! */
723 /* np_free(directories); */
724 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
725 }
726 *p = '/';
727 }
728 }
729 np_free(directories);
730 }
731
732 result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
733 if (result < 0) {
734 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
735 }
736
737 if ((fd = mkstemp(temp_file)) == -1) {
738 np_free(temp_file);
739 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
740 }
741
742 fp = (FILE *)fdopen(fd, "w");
743 if (fp == NULL) {
744 close(fd);
745 unlink(temp_file);
746 np_free(temp_file);
747 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
748 }
749
750 fprintf(fp, "# NP State file\n");
751 fprintf(fp, "%d\n", NP_STATE_FORMAT_VERSION);
752 fprintf(fp, "%d\n", this_monitoring_plugin->state->data_version);
753 fprintf(fp, "%lu\n", current_time);
754 fprintf(fp, "%s\n", data_string);
755
756 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
757
758 fflush(fp);
759
760 result = fclose(fp);
761
762 fsync(fd);
763
764 if (result != 0) {
765 unlink(temp_file);
766 np_free(temp_file);
767 die(STATE_UNKNOWN, _("Error writing temp file"));
768 }
769
770 if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) {
771 unlink(temp_file);
772 np_free(temp_file);
773 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
774 }
775
776 np_free(temp_file);
777}
diff --git a/lib/utils_base.h b/lib/utils_base.h
index 123066f8..f1c99a54 100644
--- a/lib/utils_base.h
+++ b/lib/utils_base.h
@@ -8,7 +8,6 @@
8#include "./perfdata.h" 8#include "./perfdata.h"
9#include "./thresholds.h" 9#include "./thresholds.h"
10 10
11
12#ifndef USE_OPENSSL 11#ifndef USE_OPENSSL
13# include "sha256.h" 12# include "sha256.h"
14#endif 13#endif
@@ -26,25 +25,8 @@
26#define OUTSIDE 0 25#define OUTSIDE 0
27#define INSIDE 1 26#define INSIDE 1
28 27
29#define NP_STATE_FORMAT_VERSION 1
30
31typedef struct state_data_struct {
32 time_t time;
33 void *data;
34 int length; /* Of binary data */
35} state_data;
36
37typedef struct state_key_struct {
38 char *name;
39 char *plugin_name;
40 int data_version;
41 char *_filename;
42 state_data *state_data;
43} state_key;
44
45typedef struct np_struct { 28typedef struct np_struct {
46 char *plugin_name; 29 char *plugin_name;
47 state_key *state;
48 int argc; 30 int argc;
49 char **argv; 31 char **argv;
50} monitoring_plugin; 32} monitoring_plugin;
@@ -100,10 +82,6 @@ char *np_extract_value(const char *, const char *, char);
100 */ 82 */
101int mp_translate_state(char *); 83int mp_translate_state(char *);
102 84
103void np_enable_state(char *, int);
104state_data *np_state_read(void);
105void np_state_write_string(time_t, char *);
106
107void np_init(char *, int argc, char **argv); 85void np_init(char *, int argc, char **argv);
108void np_set_args(int argc, char **argv); 86void np_set_args(int argc, char **argv);
109void np_cleanup(void); 87void np_cleanup(void);
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c
index 18350ac0..9b222409 100644
--- a/lib/utils_cmd.c
+++ b/lib/utils_cmd.c
@@ -346,7 +346,7 @@ int cmd_run_array(char *const *argv, output *out, output *err, int flags) {
346 return _cmd_close(fd); 346 return _cmd_close(fd);
347} 347}
348 348
349int cmd_file_read(char *filename, output *out, int flags) { 349int cmd_file_read(const char *filename, output *out, int flags) {
350 int fd; 350 int fd;
351 if (out) 351 if (out)
352 memset(out, 0, sizeof(output)); 352 memset(out, 0, sizeof(output));
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h
index d00069c9..728ece23 100644
--- a/lib/utils_cmd.h
+++ b/lib/utils_cmd.h
@@ -20,7 +20,7 @@ typedef struct output output;
20/** prototypes **/ 20/** prototypes **/
21int cmd_run(const char *, output *, output *, int); 21int cmd_run(const char *, output *, output *, int);
22int cmd_run_array(char *const *, output *, output *, int); 22int cmd_run_array(char *const *, output *, output *, int);
23int cmd_file_read(char *, output *, int); 23int cmd_file_read(const char *, output *, int);
24 24
25/* only multi-threaded plugins need to bother with this */ 25/* only multi-threaded plugins need to bother with this */
26void cmd_init(void); 26void cmd_init(void);
diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am
index a80229e2..b6342909 100644
--- a/plugins-root/Makefile.am
+++ b/plugins-root/Makefile.am
@@ -24,7 +24,9 @@ noinst_PROGRAMS = check_dhcp check_icmp @EXTRAS_ROOT@
24 24
25EXTRA_PROGRAMS = pst3 25EXTRA_PROGRAMS = pst3
26 26
27EXTRA_DIST = t pst3.c 27EXTRA_DIST = t pst3.c \
28 check_icmp.d \
29 check_dhcp.d
28 30
29BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a 31BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a
30NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) 32NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS)
@@ -82,6 +84,7 @@ install-exec-local: $(noinst_PROGRAMS)
82# the actual targets 84# the actual targets
83check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) 85check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO)
84check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) 86check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO)
87check_icmp_SOURCES = check_icmp.c check_icmp.d/check_icmp_helpers.c
85 88
86# -m64 needed at compiler and linker phase 89# -m64 needed at compiler and linker phase
87pst3_CFLAGS = @PST3CFLAGS@ 90pst3_CFLAGS = @PST3CFLAGS@
@@ -89,7 +92,7 @@ pst3_LDFLAGS = @PST3CFLAGS@
89# pst3 must not use monitoringplug/gnulib includes! 92# pst3 must not use monitoringplug/gnulib includes!
90pst3_CPPFLAGS = 93pst3_CPPFLAGS =
91 94
92check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) 95check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS)
93check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) 96check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS)
94 97
95clean-local: 98clean-local:
diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c
index 6802232e..daed9cb0 100644
--- a/plugins-root/check_dhcp.c
+++ b/plugins-root/check_dhcp.c
@@ -4,7 +4,7 @@
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)
7 * Copyright (c) 2001-2023 Monitoring Plugins Development Team 7 * Copyright (c) 2001-2025 Monitoring Plugins Development Team
8 * 8 *
9 * Description: 9 * Description:
10 * 10 *
@@ -34,13 +34,17 @@
34 *****************************************************************************/ 34 *****************************************************************************/
35 35
36const char *progname = "check_dhcp"; 36const char *progname = "check_dhcp";
37const char *copyright = "2001-2024"; 37const char *copyright = "2001-2025";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "../plugins/common.h"
41#include "netutils.h" 41#include "../plugins/utils.h"
42#include "utils.h" 42#include "./check_dhcp.d/config.h"
43#include "../lib/output.h"
44#include "../lib/utils_base.h"
43 45
46#include "states.h"
47#include <stdint.h>
44#include <ctype.h> 48#include <ctype.h>
45#include <stdio.h> 49#include <stdio.h>
46#include <stdlib.h> 50#include <stdlib.h>
@@ -111,8 +115,9 @@ static long mac_addr_dlpi(const char *, int, u_char *);
111 115
112/**** Common definitions ****/ 116/**** Common definitions ****/
113 117
114#define OK 0 118#define OK 0
115#define ERROR -1 119#define ERROR -1
120#define MAC_ADDR_LEN 6
116 121
117/**** DHCP definitions ****/ 122/**** DHCP definitions ****/
118 123
@@ -149,12 +154,6 @@ typedef struct dhcp_offer_struct {
149 struct dhcp_offer_struct *next; 154 struct dhcp_offer_struct *next;
150} dhcp_offer; 155} dhcp_offer;
151 156
152typedef struct requested_server_struct {
153 struct in_addr server_address;
154 bool answered;
155 struct requested_server_struct *next;
156} requested_server;
157
158#define BOOTREQUEST 1 157#define BOOTREQUEST 1
159#define BOOTREPLY 2 158#define BOOTREPLY 2
160 159
@@ -186,65 +185,60 @@ typedef struct requested_server_struct {
186#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ 185#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */
187#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ 186#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */
188 187
189static bool unicast = false; /* unicast mode: mimic a DHCP relay */
190static bool exclusive = false; /* exclusive mode aka "rogue DHCP server detection" */
191static struct in_addr my_ip; /* our address (required for relay) */
192static struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
193static unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
194static unsigned char *user_specified_mac = NULL;
195
196static char network_interface_name[IFNAMSIZ] = "eth0";
197
198static uint32_t packet_xid = 0;
199
200static uint32_t dhcp_lease_time = 0;
201static uint32_t dhcp_renewal_time = 0;
202static uint32_t dhcp_rebinding_time = 0;
203
204static int dhcpoffer_timeout = 2;
205
206static dhcp_offer *dhcp_offer_list = NULL;
207static requested_server *requested_server_list = NULL;
208
209static int valid_responses = 0; /* number of valid DHCPOFFERs we received */
210static int requested_servers = 0;
211static int requested_responses = 0;
212
213static bool request_specific_address = false;
214static bool received_requested_address = false;
215static int verbose = 0; 188static int verbose = 0;
216static struct in_addr requested_address;
217 189
218static int process_arguments(int, char **); 190typedef struct process_arguments_wrapper {
219static int call_getopt(int, char **); 191 int error;
220static int validate_arguments(int); 192 check_dhcp_config config;
193} process_arguments_wrapper;
194
195static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
221void print_usage(void); 196void print_usage(void);
222static void print_help(void); 197static void print_help(void);
223 198
224static void resolve_host(const char *in, struct in_addr *out); 199static void resolve_host(const char * /*in*/, struct in_addr * /*out*/);
225static unsigned char *mac_aton(const char *); 200static unsigned char *mac_aton(const char * /*string*/);
226static void print_hardware_address(const unsigned char *); 201static void print_hardware_address(const unsigned char * /*address*/);
227static int get_hardware_address(int, char *); 202static int get_hardware_address(int /*sock*/, char * /*interface_name*/, unsigned char *client_hardware_address);
228static int get_ip_address(int, char *); 203
229 204typedef struct get_ip_address_wrapper {
230static int send_dhcp_discover(int); 205 int error;
231static int get_dhcp_offer(int); 206 struct in_addr my_ip;
232 207} get_ip_address_wrapper;
233static int get_results(void); 208static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/);
234 209
235static int add_dhcp_offer(struct in_addr, dhcp_packet *); 210typedef struct send_dhcp_discover_wrapper {
236static int free_dhcp_offer_list(void); 211 int error;
237static int free_requested_server_list(void); 212 uint32_t packet_xid;
238 213} send_dhcp_discover_wrapper;
239static int create_dhcp_socket(void); 214static send_dhcp_discover_wrapper send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address,
240static int close_dhcp_socket(int); 215 bool request_specific_address, struct in_addr my_ip,
241static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); 216 unsigned char *client_hardware_address);
242static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); 217typedef struct get_dhcp_offer_wrapper {
218 int error;
219 int valid_responses;
220 dhcp_offer *dhcp_offer_list;
221} get_dhcp_offer_wrapper;
222static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
223 const unsigned char *client_hardware_address);
224
225static mp_subcheck get_results(bool exclusive, int requested_servers, struct in_addr requested_address, bool request_specific_address,
226 requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list);
227
228typedef struct add_dhcp_offer_wrapper {
229 int error;
230 dhcp_offer *dhcp_offer_list;
231} add_dhcp_offer_wrapper;
232static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, dhcp_packet * /*offer_packet*/, dhcp_offer *dhcp_offer_list);
233static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list);
234static int free_requested_server_list(requested_server *requested_server_list);
235
236static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name);
237static int close_dhcp_socket(int /*sock*/);
238static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, struct sockaddr_in * /*dest*/);
239static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, int /*timeout*/, struct sockaddr_in * /*address*/);
243 240
244int main(int argc, char **argv) { 241int main(int argc, char **argv) {
245 int dhcp_socket;
246 int result = STATE_UNKNOWN;
247
248 setlocale(LC_ALL, ""); 242 setlocale(LC_ALL, "");
249 bindtextdomain(PACKAGE, LOCALEDIR); 243 bindtextdomain(PACKAGE, LOCALEDIR);
250 textdomain(PACKAGE); 244 textdomain(PACKAGE);
@@ -252,43 +246,80 @@ int main(int argc, char **argv) {
252 /* Parse extra opts if any */ 246 /* Parse extra opts if any */
253 argv = np_extra_opts(&argc, argv, progname); 247 argv = np_extra_opts(&argc, argv, progname);
254 248
255 if (process_arguments(argc, argv) != OK) { 249 process_arguments_wrapper tmp = process_arguments(argc, argv);
250
251 if (tmp.error != OK) {
256 usage4(_("Could not parse arguments")); 252 usage4(_("Could not parse arguments"));
257 } 253 }
258 254
255 check_dhcp_config config = tmp.config;
256 if (config.output_format_is_set) {
257 mp_set_format(config.output_format);
258 }
259
259 /* create socket for DHCP communications */ 260 /* create socket for DHCP communications */
260 dhcp_socket = create_dhcp_socket(); 261 int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name);
261 262
262 /* get hardware address of client machine */ 263 /* get hardware address of client machine */
263 if (user_specified_mac != NULL) 264 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
264 memcpy(client_hardware_address, user_specified_mac, 6); 265 if (config.user_specified_mac != NULL) {
265 else 266 memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN);
266 get_hardware_address(dhcp_socket, network_interface_name); 267 } else {
268 get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address);
269 }
267 270
268 if (unicast) /* get IP address of client machine */ 271 struct in_addr my_ip = {0};
269 get_ip_address(dhcp_socket, network_interface_name); 272
273 if (config.unicast_mode) { /* get IP address of client machine */
274 get_ip_address_wrapper tmp_get_ip = get_ip_address(dhcp_socket, config.network_interface_name);
275 if (tmp_get_ip.error == OK) {
276 my_ip = tmp_get_ip.my_ip;
277 } else {
278 // TODO failed to get own IP
279 die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode");
280 }
281 }
270 282
271 /* send DHCPDISCOVER packet */ 283 /* send DHCPDISCOVER packet */
272 send_dhcp_discover(dhcp_socket); 284 send_dhcp_discover_wrapper disco_res = send_dhcp_discover(dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address,
285 config.request_specific_address, my_ip, client_hardware_address);
286
287 if (disco_res.error != OK) {
288 // DO something?
289 die(STATE_UNKNOWN, "Failed to send DHCP discover");
290 }
273 291
274 /* wait for a DHCPOFFER packet */ 292 /* wait for a DHCPOFFER packet */
275 get_dhcp_offer(dhcp_socket); 293 get_dhcp_offer_wrapper offer_res =
294 get_dhcp_offer(dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address);
295
296 int valid_responses = 0;
297 dhcp_offer *dhcp_offer_list = NULL;
298 if (offer_res.error == OK) {
299 valid_responses = offer_res.valid_responses;
300 dhcp_offer_list = offer_res.dhcp_offer_list;
301 } else {
302 die(STATE_UNKNOWN, "Failed to get DHCP offers");
303 }
276 304
277 /* close socket we created */ 305 /* close socket we created */
278 close_dhcp_socket(dhcp_socket); 306 close_dhcp_socket(dhcp_socket);
279 307
280 /* determine state/plugin output to return */ 308 mp_check overall = mp_check_init();
281 result = get_results();
282 309
310 /* determine state/plugin output to return */
311 mp_subcheck sc_res = get_results(config.exclusive_mode, config.num_of_requested_servers, config.requested_address,
312 config.request_specific_address, config.requested_server_list, valid_responses, dhcp_offer_list);
313 mp_add_subcheck_to_check(&overall, sc_res);
283 /* free allocated memory */ 314 /* free allocated memory */
284 free_dhcp_offer_list(); 315 free_dhcp_offer_list(dhcp_offer_list);
285 free_requested_server_list(); 316 free_requested_server_list(config.requested_server_list);
286 317
287 return result; 318 mp_exit(overall);
288} 319}
289 320
290/* determines hardware address on client machine */ 321/* determines hardware address on client machine */
291static int get_hardware_address(int sock, char *interface_name) { 322int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) {
292 323
293#if defined(__linux__) 324#if defined(__linux__)
294 struct ifreq ifr; 325 struct ifreq ifr;
@@ -302,7 +333,7 @@ static int get_hardware_address(int sock, char *interface_name) {
302 exit(STATE_UNKNOWN); 333 exit(STATE_UNKNOWN);
303 } 334 }
304 335
305 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); 336 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN);
306 337
307#elif defined(__bsd__) 338#elif defined(__bsd__)
308 /* King 2004 see ACKNOWLEDGEMENTS */ 339 /* King 2004 see ACKNOWLEDGEMENTS */
@@ -358,8 +389,9 @@ static int get_hardware_address(int sock, char *interface_name) {
358 int i; 389 int i;
359 p = interface_name + strlen(interface_name) - 1; 390 p = interface_name + strlen(interface_name) - 1;
360 for (i = strlen(interface_name) - 1; i > 0; p--) { 391 for (i = strlen(interface_name) - 1; i > 0; p--) {
361 if (isalpha(*p)) 392 if (isalpha(*p)) {
362 break; 393 break;
394 }
363 } 395 }
364 p++; 396 p++;
365 if (p != interface_name) { 397 if (p != interface_name) {
@@ -393,14 +425,15 @@ static int get_hardware_address(int sock, char *interface_name) {
393 exit(STATE_UNKNOWN); 425 exit(STATE_UNKNOWN);
394#endif 426#endif
395 427
396 if (verbose) 428 if (verbose) {
397 print_hardware_address(client_hardware_address); 429 print_hardware_address(client_hardware_address);
430 }
398 431
399 return OK; 432 return OK;
400} 433}
401 434
402/* determines IP address of the client interface */ 435/* determines IP address of the client interface */
403static int get_ip_address(int sock, char *interface_name) { 436get_ip_address_wrapper get_ip_address(int sock, char *interface_name) {
404#if defined(SIOCGIFADDR) 437#if defined(SIOCGIFADDR)
405 struct ifreq ifr; 438 struct ifreq ifr;
406 439
@@ -412,28 +445,28 @@ static int get_ip_address(int sock, char *interface_name) {
412 exit(STATE_UNKNOWN); 445 exit(STATE_UNKNOWN);
413 } 446 }
414 447
415 my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
416
417#else 448#else
418 printf(_("Error: Cannot get interface IP address on this platform.\n")); 449 printf(_("Error: Cannot get interface IP address on this platform.\n"));
419 exit(STATE_UNKNOWN); 450 exit(STATE_UNKNOWN);
420#endif 451#endif
421 452
422 if (verbose) 453 get_ip_address_wrapper result = {
423 printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); 454 .error = OK,
455 .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr,
456 };
424 457
425 return OK; 458 if (verbose) {
459 printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip));
460 }
461
462 return result;
426} 463}
427 464
428/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ 465/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
429static int send_dhcp_discover(int sock) { 466static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address,
430 dhcp_packet discover_packet; 467 bool request_specific_address, struct in_addr my_ip,
431 struct sockaddr_in sockaddr_broadcast; 468 unsigned char *client_hardware_address) {
432 unsigned short opts; 469 dhcp_packet discover_packet = {0};
433
434 /* clear the packet data structure */
435 bzero(&discover_packet, sizeof(discover_packet));
436
437 /* boot request flag (backward compatible with BOOTP servers) */ 470 /* boot request flag (backward compatible with BOOTP servers) */
438 discover_packet.op = BOOTREQUEST; 471 discover_packet.op = BOOTREQUEST;
439 472
@@ -443,12 +476,15 @@ static int send_dhcp_discover(int sock) {
443 /* length of our hardware address */ 476 /* length of our hardware address */
444 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; 477 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH;
445 478
479 send_dhcp_discover_wrapper result = {
480 .error = OK,
481 };
446 /* 482 /*
447 * transaction ID is supposed to be random. 483 * transaction ID is supposed to be random.
448 */ 484 */
449 srand(time(NULL) ^ getpid()); 485 srand(time(NULL) ^ getpid());
450 packet_xid = random(); 486 result.packet_xid = random();
451 discover_packet.xid = htonl(packet_xid); 487 discover_packet.xid = htonl(result.packet_xid);
452 488
453 /*discover_packet.secs=htons(65535);*/ 489 /*discover_packet.secs=htons(65535);*/
454 discover_packet.secs = 0xFF; 490 discover_packet.secs = 0xFF;
@@ -468,7 +504,7 @@ static int send_dhcp_discover(int sock) {
468 discover_packet.options[2] = '\x53'; 504 discover_packet.options[2] = '\x53';
469 discover_packet.options[3] = '\x63'; 505 discover_packet.options[3] = '\x63';
470 506
471 opts = 4; 507 unsigned short opts = 4;
472 /* DHCP message type is embedded in options field */ 508 /* DHCP message type is embedded in options field */
473 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ 509 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
474 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ 510 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */
@@ -484,17 +520,19 @@ static int send_dhcp_discover(int sock) {
484 discover_packet.options[opts++] = (char)DHCP_OPTION_END; 520 discover_packet.options[opts++] = (char)DHCP_OPTION_END;
485 521
486 /* unicast fields */ 522 /* unicast fields */
487 if (unicast) 523 if (unicast) {
488 discover_packet.giaddr.s_addr = my_ip.s_addr; 524 discover_packet.giaddr.s_addr = my_ip.s_addr;
525 }
489 526
490 /* see RFC 1542, 4.1.1 */ 527 /* see RFC 1542, 4.1.1 */
491 discover_packet.hops = unicast ? 1 : 0; 528 discover_packet.hops = unicast ? 1 : 0;
492 529
493 /* send the DHCPDISCOVER packet to broadcast address */ 530 /* send the DHCPDISCOVER packet to broadcast address */
494 sockaddr_broadcast.sin_family = AF_INET; 531 struct sockaddr_in sockaddr_broadcast = {
495 sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); 532 .sin_family = AF_INET,
496 sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; 533 .sin_port = htons(DHCP_SERVER_PORT),
497 bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); 534 .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST,
535 };
498 536
499 if (verbose) { 537 if (verbose) {
500 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); 538 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port));
@@ -508,56 +546,56 @@ static int send_dhcp_discover(int sock) {
508 /* send the DHCPDISCOVER packet out */ 546 /* send the DHCPDISCOVER packet out */
509 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); 547 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast);
510 548
511 if (verbose) 549 if (verbose) {
512 printf("\n\n"); 550 printf("\n\n");
551 }
513 552
514 return OK; 553 return result;
515} 554}
516 555
517/* waits for a DHCPOFFER message from one or more DHCP servers */ 556/* waits for a DHCPOFFER message from one or more DHCP servers */
518static int get_dhcp_offer(int sock) { 557get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
519 dhcp_packet offer_packet; 558 const unsigned char *client_hardware_address) {
520 struct sockaddr_in source;
521 struct sockaddr_in via;
522 int result = OK;
523 int responses = 0;
524 int x;
525 time_t start_time; 559 time_t start_time;
526 time_t current_time;
527
528 time(&start_time); 560 time(&start_time);
529 561
562 int result = OK;
563 int responses = 0;
564 int valid_responses = 0;
530 /* receive as many responses as we can */ 565 /* receive as many responses as we can */
531 for (responses = 0, valid_responses = 0;;) { 566 for (;;) {
532 567 time_t current_time;
533 time(&current_time); 568 time(&current_time);
534 if ((current_time - start_time) >= dhcpoffer_timeout) 569 if ((current_time - start_time) >= dhcpoffer_timeout) {
535 break; 570 break;
571 }
536 572
537 if (verbose) 573 if (verbose) {
538 printf("\n\n"); 574 printf("\n\n");
575 }
539 576
540 bzero(&source, sizeof(source)); 577 struct sockaddr_in source = {0};
541 bzero(&via, sizeof(via)); 578 dhcp_packet offer_packet = {0};
542 bzero(&offer_packet, sizeof(offer_packet));
543 579
544 result = OK; 580 result = OK;
545 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); 581 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source);
546 582
547 if (result != OK) { 583 if (result != OK) {
548 if (verbose) 584 if (verbose) {
549 printf(_("Result=ERROR\n")); 585 printf(_("Result=ERROR\n"));
586 }
550 587
551 continue; 588 continue;
552 } else { 589 }
553 if (verbose) 590 if (verbose) {
554 printf(_("Result=OK\n")); 591 printf(_("Result=OK\n"));
555
556 responses++;
557 } 592 }
558 593
594 responses++;
595
559 /* The "source" is either a server or a relay. */ 596 /* The "source" is either a server or a relay. */
560 /* Save a copy of "source" into "via" even if it's via itself */ 597 /* Save a copy of "source" into "via" even if it's via itself */
598 struct sockaddr_in via = {0};
561 memcpy(&via, &source, sizeof(source)); 599 memcpy(&via, &source, sizeof(source));
562 600
563 if (verbose) { 601 if (verbose) {
@@ -568,30 +606,37 @@ static int get_dhcp_offer(int sock) {
568 606
569 /* check packet xid to see if its the same as the one we used in the discover packet */ 607 /* check packet xid to see if its the same as the one we used in the discover packet */
570 if (ntohl(offer_packet.xid) != packet_xid) { 608 if (ntohl(offer_packet.xid) != packet_xid) {
571 if (verbose) 609 if (verbose) {
572 printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), packet_xid); 610 printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid),
611 packet_xid);
612 }
573 613
574 continue; 614 continue;
575 } 615 }
576 616
577 /* check hardware address */ 617 /* check hardware address */
578 result = OK; 618 result = OK;
579 if (verbose) 619 if (verbose) {
580 printf("DHCPOFFER chaddr: "); 620 printf("DHCPOFFER chaddr: ");
621 }
581 622
582 for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { 623 for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) {
583 if (verbose) 624 if (verbose) {
584 printf("%02X", (unsigned char)offer_packet.chaddr[x]); 625 printf("%02X", offer_packet.chaddr[i]);
626 }
585 627
586 if (offer_packet.chaddr[x] != client_hardware_address[x]) 628 if (offer_packet.chaddr[i] != client_hardware_address[i]) {
587 result = ERROR; 629 result = ERROR;
630 }
588 } 631 }
589 if (verbose) 632 if (verbose) {
590 printf("\n"); 633 printf("\n");
634 }
591 635
592 if (result == ERROR) { 636 if (result == ERROR) {
593 if (verbose) 637 if (verbose) {
594 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); 638 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
639 }
595 640
596 continue; 641 continue;
597 } 642 }
@@ -603,7 +648,12 @@ static int get_dhcp_offer(int sock) {
603 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); 648 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr));
604 } 649 }
605 650
606 add_dhcp_offer(source.sin_addr, &offer_packet); 651 add_dhcp_offer_wrapper add_res = add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list);
652 if (add_res.error != OK) {
653 // TODO
654 } else {
655 dhcp_offer_list = add_res.dhcp_offer_list;
656 }
607 657
608 valid_responses++; 658 valid_responses++;
609 } 659 }
@@ -613,104 +663,101 @@ static int get_dhcp_offer(int sock) {
613 printf(_("Valid responses for this machine: %d\n"), valid_responses); 663 printf(_("Valid responses for this machine: %d\n"), valid_responses);
614 } 664 }
615 665
616 return OK; 666 get_dhcp_offer_wrapper ret_val = {
667 .error = OK,
668 .valid_responses = valid_responses,
669 .dhcp_offer_list = dhcp_offer_list,
670 };
671 return ret_val;
617} 672}
618 673
619/* sends a DHCP packet */ 674/* sends a DHCP packet */
620static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { 675int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) {
621 int result; 676 int result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
622
623 result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
624 677
625 if (verbose) 678 if (verbose) {
626 printf(_("send_dhcp_packet result: %d\n"), result); 679 printf(_("send_dhcp_packet result: %d\n"), result);
680 }
627 681
628 if (result < 0) 682 if (result < 0) {
629 return ERROR; 683 return ERROR;
684 }
630 685
631 return OK; 686 return OK;
632} 687}
633 688
634/* receives a DHCP packet */ 689/* receives a DHCP packet */
635static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { 690int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) {
636 struct timeval tv;
637 fd_set readfds;
638 fd_set oobfds;
639 int recv_result;
640 socklen_t address_size;
641 struct sockaddr_in source_address;
642 int nfound;
643
644 /* wait for data to arrive (up time timeout) */ 691 /* wait for data to arrive (up time timeout) */
645 tv.tv_sec = timeout; 692 struct timeval timeout_val = {
646 tv.tv_usec = 0; 693 .tv_sec = timeout,
694 .tv_usec = 0,
695 };
696 fd_set readfds;
647 FD_ZERO(&readfds); 697 FD_ZERO(&readfds);
698 fd_set oobfds;
648 FD_ZERO(&oobfds); 699 FD_ZERO(&oobfds);
649 FD_SET(sock, &readfds); 700 FD_SET(sock, &readfds);
650 FD_SET(sock, &oobfds); 701 FD_SET(sock, &oobfds);
651 nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); 702 int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val);
652 703
653 /* make sure some data has arrived */ 704 /* make sure some data has arrived */
654 if (!FD_ISSET(sock, &readfds)) { 705 if (!FD_ISSET(sock, &readfds)) {
655 if (verbose) 706 if (verbose) {
656 printf(_("No (more) data received (nfound: %d)\n"), nfound); 707 printf(_("No (more) data received (nfound: %d)\n"), nfound);
708 }
657 return ERROR; 709 return ERROR;
658 } 710 }
659 711
660 else { 712 struct sockaddr_in source_address = {0};
661 bzero(&source_address, sizeof(source_address)); 713 socklen_t address_size = sizeof(source_address);
662 address_size = sizeof(source_address); 714 int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size);
663 recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); 715 if (verbose) {
664 if (verbose) 716 printf("recv_result: %d\n", recv_result);
665 printf("recv_result: %d\n", recv_result); 717 }
666
667 if (recv_result == -1) {
668 if (verbose) {
669 printf(_("recvfrom() failed, "));
670 printf("errno: (%d) -> %s\n", errno, strerror(errno));
671 }
672 return ERROR;
673 } else {
674 if (verbose) {
675 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
676 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
677 }
678 718
679 memcpy(address, &source_address, sizeof(source_address)); 719 if (recv_result == -1) {
680 return OK; 720 if (verbose) {
721 printf(_("recvfrom() failed, "));
722 printf("errno: (%d) -> %s\n", errno, strerror(errno));
681 } 723 }
724 return ERROR;
725 }
726 if (verbose) {
727 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
728 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
682 } 729 }
683 730
731 memcpy(address, &source_address, sizeof(source_address));
684 return OK; 732 return OK;
685} 733}
686 734
687/* creates a socket for DHCP communication */ 735/* creates a socket for DHCP communication */
688static int create_dhcp_socket(void) { 736int create_dhcp_socket(bool unicast, char *network_interface_name) {
689 struct sockaddr_in myname;
690 struct ifreq interface;
691 int sock;
692 int flag = 1;
693
694 /* Set up the address we're going to bind to. */ 737 /* Set up the address we're going to bind to. */
695 bzero(&myname, sizeof(myname));
696 myname.sin_family = AF_INET;
697 /* listen to DHCP server port if we're in unicast mode */ 738 /* listen to DHCP server port if we're in unicast mode */
698 myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); 739 struct sockaddr_in myname = {
699 myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; 740 .sin_family = AF_INET,
700 bzero(&myname.sin_zero, sizeof(myname.sin_zero)); 741 .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT),
742 // TODO previously the next line was trying to use our own IP, we was not set
743 // until some point later, so it was removed. Recheck whether it is actually
744 // necessary/useful
745 .sin_addr.s_addr = INADDR_ANY,
746 };
701 747
702 /* create a socket for DHCP communications */ 748 /* create a socket for DHCP communications */
703 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 749 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
704 if (sock < 0) { 750 if (sock < 0) {
705 printf(_("Error: Could not create socket!\n")); 751 printf(_("Error: Could not create socket!\n"));
706 exit(STATE_UNKNOWN); 752 exit(STATE_UNKNOWN);
707 } 753 }
708 754
709 if (verbose) 755 if (verbose) {
710 printf("DHCP socket: %d\n", sock); 756 printf("DHCP socket: %d\n", sock);
757 }
711 758
712 /* set the reuse address flag so we don't get errors when restarting */ 759 /* set the reuse address flag so we don't get errors when restarting */
713 flag = 1; 760 int flag = 1;
714 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { 761 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) {
715 printf(_("Error: Could not set reuse address option on DHCP socket!\n")); 762 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
716 exit(STATE_UNKNOWN); 763 exit(STATE_UNKNOWN);
@@ -722,6 +769,7 @@ static int create_dhcp_socket(void) {
722 exit(STATE_UNKNOWN); 769 exit(STATE_UNKNOWN);
723 } 770 }
724 771
772 struct ifreq interface;
725 /* bind socket to interface */ 773 /* bind socket to interface */
726#if defined(__linux__) 774#if defined(__linux__)
727 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); 775 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1);
@@ -746,105 +794,116 @@ static int create_dhcp_socket(void) {
746} 794}
747 795
748/* closes DHCP socket */ 796/* closes DHCP socket */
749static int close_dhcp_socket(int sock) { 797int close_dhcp_socket(int sock) {
750
751 close(sock); 798 close(sock);
752
753 return OK; 799 return OK;
754} 800}
755 801
756/* adds a requested server address to list in memory */ 802/* adds a requested server address to list in memory */
757static int add_requested_server(struct in_addr server_address) { 803int add_requested_server(struct in_addr server_address, int *requested_servers, requested_server **requested_server_list) {
758 requested_server *new_server; 804 requested_server *new_server = (requested_server *)malloc(sizeof(requested_server));
759 805 if (new_server == NULL) {
760 new_server = (requested_server *)malloc(sizeof(requested_server));
761 if (new_server == NULL)
762 return ERROR; 806 return ERROR;
807 }
763 808
764 new_server->server_address = server_address; 809 new_server->server_address = server_address;
765 new_server->answered = false; 810 new_server->answered = false;
766 811
767 new_server->next = requested_server_list; 812 new_server->next = *requested_server_list;
768 requested_server_list = new_server; 813 *requested_server_list = new_server;
769 814
770 requested_servers++; 815 *requested_servers += 1;
771 816
772 if (verbose) 817 if (verbose) {
773 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); 818 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address));
819 }
774 820
775 return OK; 821 return OK;
776} 822}
777 823
778/* adds a DHCP OFFER to list in memory */ 824/* adds a DHCP OFFER to list in memory */
779static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { 825add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, dhcp_offer *dhcp_offer_list) {
826 if (offer_packet == NULL) {
827 add_dhcp_offer_wrapper tmp = {
828 .error = ERROR,
829 };
830 return tmp;
831 }
832
833 uint32_t dhcp_lease_time = 0;
834 uint32_t dhcp_renewal_time = 0;
835 uint32_t dhcp_rebinding_time = 0;
780 dhcp_offer *new_offer; 836 dhcp_offer *new_offer;
781 int x;
782 unsigned option_type;
783 unsigned option_length;
784 struct in_addr serv_ident = {0}; 837 struct in_addr serv_ident = {0};
785
786 if (offer_packet == NULL)
787 return ERROR;
788
789 /* process all DHCP options present in the packet */ 838 /* process all DHCP options present in the packet */
790 for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { 839 for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) {
791 840
792 if ((int)offer_packet->options[x] == -1) 841 if ((int)offer_packet->options[dchp_opt_idx] == -1) {
793 break; 842 break;
843 }
794 844
795 /* get option type */ 845 /* get option type */
796 option_type = offer_packet->options[x++]; 846 unsigned option_type = offer_packet->options[dchp_opt_idx++];
797 847
798 /* get option length */ 848 /* get option length */
799 option_length = offer_packet->options[x++]; 849 unsigned option_length = offer_packet->options[dchp_opt_idx++];
800 850
801 if (verbose) 851 if (verbose) {
802 printf("Option: %d (0x%02X)\n", option_type, option_length); 852 printf("Option: %d (0x%02X)\n", option_type, option_length);
853 }
803 854
804 /* get option data */ 855 /* get option data */
805 switch (option_type) { 856 switch (option_type) {
806 case DHCP_OPTION_LEASE_TIME: 857 case DHCP_OPTION_LEASE_TIME:
807 memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); 858 memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time));
808 dhcp_lease_time = ntohl(dhcp_lease_time); 859 dhcp_lease_time = ntohl(dhcp_lease_time);
809 break; 860 break;
810 case DHCP_OPTION_RENEWAL_TIME: 861 case DHCP_OPTION_RENEWAL_TIME:
811 memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); 862 memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_renewal_time));
812 dhcp_renewal_time = ntohl(dhcp_renewal_time); 863 dhcp_renewal_time = ntohl(dhcp_renewal_time);
813 break; 864 break;
814 case DHCP_OPTION_REBINDING_TIME: 865 case DHCP_OPTION_REBINDING_TIME:
815 memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); 866 memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_rebinding_time));
816 dhcp_rebinding_time = ntohl(dhcp_rebinding_time); 867 dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
817 break; 868 break;
818 case DHCP_OPTION_SERVER_IDENTIFIER: 869 case DHCP_OPTION_SERVER_IDENTIFIER:
819 memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); 870 memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], sizeof(serv_ident.s_addr));
820 break; 871 break;
821 } 872 }
822 873
823 /* skip option data we're ignoring */ 874 /* skip option data we're ignoring */
824 if (option_type == 0) /* "pad" option, see RFC 2132 (3.1) */ 875 if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */
825 x += 1; 876 dchp_opt_idx += 1;
826 else 877 } else {
827 x += option_length; 878 dchp_opt_idx += option_length;
879 }
828 } 880 }
829 881
830 if (verbose) { 882 if (verbose) {
831 if (dhcp_lease_time == DHCP_INFINITE_TIME) 883 if (dhcp_lease_time == DHCP_INFINITE_TIME) {
832 printf(_("Lease Time: Infinite\n")); 884 printf(_("Lease Time: Infinite\n"));
833 else 885 } else {
834 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); 886 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time);
835 if (dhcp_renewal_time == DHCP_INFINITE_TIME) 887 }
888 if (dhcp_renewal_time == DHCP_INFINITE_TIME) {
836 printf(_("Renewal Time: Infinite\n")); 889 printf(_("Renewal Time: Infinite\n"));
837 else 890 } else {
838 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); 891 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time);
839 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) 892 }
893 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) {
840 printf(_("Rebinding Time: Infinite\n")); 894 printf(_("Rebinding Time: Infinite\n"));
895 }
841 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); 896 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time);
842 } 897 }
843 898
844 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); 899 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer));
845 900
846 if (new_offer == NULL) 901 if (new_offer == NULL) {
847 return ERROR; 902 add_dhcp_offer_wrapper tmp = {
903 .error = ERROR,
904 };
905 return tmp;
906 }
848 907
849 /* 908 /*
850 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the 909 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the
@@ -874,15 +933,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
874 new_offer->next = dhcp_offer_list; 933 new_offer->next = dhcp_offer_list;
875 dhcp_offer_list = new_offer; 934 dhcp_offer_list = new_offer;
876 935
877 return OK; 936 add_dhcp_offer_wrapper result = {
937 .error = OK,
938 .dhcp_offer_list = dhcp_offer_list,
939 };
940
941 return result;
878} 942}
879 943
880/* frees memory allocated to DHCP OFFER list */ 944/* frees memory allocated to DHCP OFFER list */
881static int free_dhcp_offer_list(void) { 945int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) {
882 dhcp_offer *this_offer;
883 dhcp_offer *next_offer; 946 dhcp_offer *next_offer;
884 947 for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
885 for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
886 next_offer = this_offer->next; 948 next_offer = this_offer->next;
887 free(this_offer); 949 free(this_offer);
888 } 950 }
@@ -891,11 +953,9 @@ static int free_dhcp_offer_list(void) {
891} 953}
892 954
893/* frees memory allocated to requested server list */ 955/* frees memory allocated to requested server list */
894static int free_requested_server_list(void) { 956int free_requested_server_list(requested_server *requested_server_list) {
895 requested_server *this_server;
896 requested_server *next_server; 957 requested_server *next_server;
897 958 for (requested_server *this_server = requested_server_list; this_server != NULL; this_server = next_server) {
898 for (this_server = requested_server_list; this_server != NULL; this_server = next_server) {
899 next_server = this_server->next; 959 next_server = this_server->next;
900 free(this_server); 960 free(this_server);
901 } 961 }
@@ -904,39 +964,60 @@ static int free_requested_server_list(void) {
904} 964}
905 965
906/* gets state and plugin output to return */ 966/* gets state and plugin output to return */
907static int get_results(void) { 967mp_subcheck get_results(bool exclusive, const int requested_servers, const struct in_addr requested_address, bool request_specific_address,
908 dhcp_offer *temp_offer, *undesired_offer = NULL; 968 requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list) {
909 requested_server *temp_server; 969 mp_subcheck sc_dhcp_results = mp_subcheck_init();
910 int result; 970 sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK);
911 uint32_t max_lease_time = 0;
912 971
913 received_requested_address = false; 972 /* we didn't receive any DHCPOFFERs */
914 973 if (dhcp_offer_list == NULL) {
915 /* checks responses from requested servers */ 974 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
916 requested_responses = 0; 975 xasprintf(&sc_dhcp_results.output, "%s", "No DHCPOFFERs were received");
917 if (requested_servers > 0) { 976 return sc_dhcp_results;
977 }
918 978
919 for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { 979 if (valid_responses == 0) {
980 // No valid responses at all, so early exit here
981 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
982 xasprintf(&sc_dhcp_results.output, "No valid responses received");
983 return sc_dhcp_results;
984 }
920 985
921 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 986 if (valid_responses == 1) {
987 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses);
988 } else {
989 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses);
990 }
922 991
992 bool received_requested_address = false;
993 dhcp_offer *undesired_offer = NULL;
994 uint32_t max_lease_time = 0;
995 /* checks responses from requested servers */
996 int requested_responses = 0;
997 if (requested_servers > 0) {
998 for (requested_server *temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) {
999 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
923 /* get max lease time we were offered */ 1000 /* get max lease time we were offered */
924 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1001 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) {
925 max_lease_time = temp_offer->lease_time; 1002 max_lease_time = temp_offer->lease_time;
1003 }
926 1004
927 /* see if we got the address we requested */ 1005 /* see if we got the address we requested */
928 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1006 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) {
929 received_requested_address = true; 1007 received_requested_address = true;
1008 }
930 1009
931 /* see if the servers we wanted a response from talked to us or not */ 1010 /* see if the servers we wanted a response from, talked to us or not */
932 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { 1011 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) {
933 if (verbose) { 1012 if (verbose) {
934 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); 1013 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address));
935 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); 1014 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address));
936 if (temp_server->answered) 1015 if (temp_server->answered) {
937 printf(_(" (duplicate)")); 1016 printf(_(" (duplicate)"));
1017 }
938 printf(_("\n")); 1018 printf(_("\n"));
939 } 1019 }
1020
940 if (!temp_server->answered) { 1021 if (!temp_server->answered) {
941 requested_responses++; 1022 requested_responses++;
942 temp_server->answered = true; 1023 temp_server->answered = true;
@@ -947,94 +1028,115 @@ static int get_results(void) {
947 } 1028 }
948 1029
949 /* exclusive mode: check for undesired offers */ 1030 /* exclusive mode: check for undesired offers */
950 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1031 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
951 if (!temp_offer->desired) { 1032 if (!temp_offer->desired) {
952 undesired_offer = temp_offer; /* Checks only for the first undesired offer */ 1033 undesired_offer = temp_offer; /* Checks only for the first undesired offer */
953 break; /* no further checks needed */ 1034 break; /* no further checks needed */
954 } 1035 }
955 } 1036 }
956 }
957 1037
958 /* else check and see if we got our requested address from any server */ 1038 mp_subcheck sc_rqust_srvs = mp_subcheck_init();
959 else { 1039 xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", requested_responses, requested_servers);
960 1040
961 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1041 if (requested_responses == requested_servers) {
1042 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK);
1043 } else if (requested_responses == 0) {
1044 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL);
1045 } else if (requested_responses < requested_servers) {
1046 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1047 } else {
1048 // We received more(!) responses than we asked for?
1049 // This case shouldn't happen, but is here for completion
1050 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1051 }
1052 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs);
962 1053
1054 } else {
1055 /* else check and see if we got our requested address from any server */
1056 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
963 /* get max lease time we were offered */ 1057 /* get max lease time we were offered */
964 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1058 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) {
965 max_lease_time = temp_offer->lease_time; 1059 max_lease_time = temp_offer->lease_time;
1060 }
966 1061
967 /* see if we got the address we requested */ 1062 /* see if we got the address we requested */
968 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1063 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) {
969 received_requested_address = true; 1064 received_requested_address = true;
1065 }
970 } 1066 }
971 } 1067 }
972 1068
973 result = STATE_OK; 1069 if (max_lease_time == DHCP_INFINITE_TIME) {
974 if (valid_responses == 0) 1070 xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output);
975 result = STATE_CRITICAL; 1071 } else {
976 else if (requested_servers > 0 && requested_responses == 0) 1072 xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", sc_dhcp_results.output, max_lease_time);
977 result = STATE_CRITICAL;
978 else if (requested_responses < requested_servers)
979 result = STATE_WARNING;
980 else if (request_specific_address && !received_requested_address)
981 result = STATE_WARNING;
982
983 if (exclusive && undesired_offer)
984 result = STATE_CRITICAL;
985
986 if (result == 0) /* garrett honeycutt 2005 */
987 printf("OK: ");
988 else if (result == 1)
989 printf("WARNING: ");
990 else if (result == 2)
991 printf("CRITICAL: ");
992 else if (result == 3)
993 printf("UNKNOWN: ");
994
995 /* we didn't receive any DHCPOFFERs */
996 if (dhcp_offer_list == NULL) {
997 printf(_("No DHCPOFFERs were received.\n"));
998 return result;
999 } 1073 }
1000 1074
1001 printf(_("Received %d DHCPOFFER(s)"), valid_responses); 1075 if (exclusive) {
1076 mp_subcheck sc_rogue_server = mp_subcheck_init();
1002 1077
1003 if (exclusive && undesired_offer) { 1078 if (undesired_offer != NULL) {
1004 printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); 1079 // We wanted to get a DHCPOFFER exclusively from one machine, but another one
1005 printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); 1080 // sent one (too)
1006 return result; 1081 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL);
1007 }
1008 1082
1009 if (requested_servers > 0) 1083 // Get the addresses for printout
1010 printf(_(", %s%d of %d requested servers responded"), ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, 1084 // 1.address of the sending server
1011 requested_servers); 1085 char server_address[INET_ADDRSTRLEN];
1086 const char *server_address_transformed =
1087 inet_ntop(AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address));
1012 1088
1013 if (request_specific_address) 1089 if (server_address != server_address_transformed) {
1014 printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); 1090 die(STATE_UNKNOWN, "inet_ntop failed");
1091 }
1015 1092
1016 printf(_(", max lease time = ")); 1093 // 2.address offered
1017 if (max_lease_time == DHCP_INFINITE_TIME) 1094 char offered_address[INET_ADDRSTRLEN];
1018 printf(_("Infinity")); 1095 const char *offered_address_transformed =
1019 else 1096 inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, sizeof(offered_address));
1020 printf("%lu sec", (unsigned long)max_lease_time);
1021 1097
1022 printf(".\n"); 1098 if (offered_address != offered_address_transformed) {
1099 die(STATE_UNKNOWN, "inet_ntop failed");
1100 }
1023 1101
1024 return result; 1102 xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", server_address, offered_address);
1103 } else {
1104 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK);
1105 xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected");
1106 }
1107 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server);
1108 }
1109
1110 if (request_specific_address) {
1111 mp_subcheck sc_rqustd_addr = mp_subcheck_init();
1112
1113 if (received_requested_address) {
1114 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK);
1115 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", inet_ntoa(requested_address));
1116 } else {
1117 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING);
1118 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", inet_ntoa(requested_address));
1119 }
1120
1121 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr);
1122 }
1123
1124 return sc_dhcp_results;
1025} 1125}
1026 1126
1027/* process command-line arguments */ 1127/* process command-line arguments */
1028static int process_arguments(int argc, char **argv) { 1128process_arguments_wrapper process_arguments(int argc, char **argv) {
1029 if (argc < 1) 1129 if (argc < 1) {
1030 return ERROR; 1130 process_arguments_wrapper tmp = {
1131 .error = ERROR,
1132 };
1133 return tmp;
1134 }
1031 1135
1032 call_getopt(argc, argv); 1136 enum {
1033 return validate_arguments(argc); 1137 output_format_index = CHAR_MAX + 1,
1034} 1138 };
1035 1139
1036static int call_getopt(int argc, char **argv) {
1037 extern int optind;
1038 int option_index = 0; 1140 int option_index = 0;
1039 static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, 1141 static struct option long_options[] = {{"serverip", required_argument, 0, 's'},
1040 {"requestedip", required_argument, 0, 'r'}, 1142 {"requestedip", required_argument, 0, 'r'},
@@ -1046,61 +1148,55 @@ static int call_getopt(int argc, char **argv) {
1046 {"verbose", no_argument, 0, 'v'}, 1148 {"verbose", no_argument, 0, 'v'},
1047 {"version", no_argument, 0, 'V'}, 1149 {"version", no_argument, 0, 'V'},
1048 {"help", no_argument, 0, 'h'}, 1150 {"help", no_argument, 0, 'h'},
1151 {"output-format", required_argument, 0, output_format_index},
1049 {0, 0, 0, 0}}; 1152 {0, 0, 0, 0}};
1050 1153
1051 int c = 0; 1154 check_dhcp_config config = check_dhcp_config_init();
1155 int option_char = 0;
1052 while (true) { 1156 while (true) {
1053 c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); 1157 option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index);
1054 1158
1055 if (c == -1 || c == EOF || c == 1) 1159 if (option_char == -1 || option_char == EOF || option_char == 1) {
1056 break; 1160 break;
1161 }
1057 1162
1058 switch (c) { 1163 switch (option_char) {
1059
1060 case 's': /* DHCP server address */ 1164 case 's': /* DHCP server address */
1061 resolve_host(optarg, &dhcp_ip); 1165 resolve_host(optarg, &config.dhcp_ip);
1062 add_requested_server(dhcp_ip); 1166 add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, &config.requested_server_list);
1063 break; 1167 break;
1064 1168
1065 case 'r': /* address we are requested from DHCP servers */ 1169 case 'r': /* address we are requested from DHCP servers */
1066 resolve_host(optarg, &requested_address); 1170 resolve_host(optarg, &config.requested_address);
1067 request_specific_address = true; 1171 config.request_specific_address = true;
1068 break; 1172 break;
1069 1173
1070 case 't': /* timeout */ 1174 case 't': /* timeout */
1071 1175 if (atoi(optarg) > 0) {
1072 /* 1176 config.dhcpoffer_timeout = atoi(optarg);
1073 if(is_intnonneg(optarg)) 1177 }
1074 */
1075 if (atoi(optarg) > 0)
1076 dhcpoffer_timeout = atoi(optarg);
1077 /*
1078 else
1079 usage("Time interval must be a nonnegative integer\n");
1080 */
1081 break; 1178 break;
1082 1179
1083 case 'm': /* MAC address */ 1180 case 'm': /* MAC address */
1084 1181 if ((config.user_specified_mac = mac_aton(optarg)) == NULL) {
1085 if ((user_specified_mac = mac_aton(optarg)) == NULL)
1086 usage("Cannot parse MAC address.\n"); 1182 usage("Cannot parse MAC address.\n");
1087 if (verbose) 1183 }
1088 print_hardware_address(user_specified_mac); 1184 if (verbose) {
1089 1185 print_hardware_address(config.user_specified_mac);
1186 }
1090 break; 1187 break;
1091 1188
1092 case 'i': /* interface name */ 1189 case 'i': /* interface name */
1093 1190 strncpy(config.network_interface_name, optarg, sizeof(config.network_interface_name) - 1);
1094 strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); 1191 config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0';
1095 network_interface_name[sizeof(network_interface_name) - 1] = '\x0';
1096
1097 break; 1192 break;
1098 1193
1099 case 'u': /* unicast testing */ 1194 case 'u': /* unicast testing */
1100 unicast = true; 1195 config.unicast_mode = true;
1101 break; 1196 break;
1197
1102 case 'x': /* exclusive testing aka "rogue DHCP server detection" */ 1198 case 'x': /* exclusive testing aka "rogue DHCP server detection" */
1103 exclusive = true; 1199 config.exclusive_mode = true;
1104 break; 1200 break;
1105 1201
1106 case 'V': /* version */ 1202 case 'V': /* version */
@@ -1114,6 +1210,18 @@ static int call_getopt(int argc, char **argv) {
1114 case 'v': /* verbose */ 1210 case 'v': /* verbose */
1115 verbose = 1; 1211 verbose = 1;
1116 break; 1212 break;
1213 case output_format_index: {
1214 parsed_output_format parser = mp_parse_output_format(optarg);
1215 if (!parser.parsing_success) {
1216 // TODO List all available formats here, maybe add anothoer usage function
1217 printf("Invalid output format: %s\n", optarg);
1218 exit(STATE_UNKNOWN);
1219 }
1220
1221 config.output_format_is_set = true;
1222 config.output_format = parser.output_format;
1223 break;
1224 }
1117 case '?': /* help */ 1225 case '?': /* help */
1118 usage5(); 1226 usage5();
1119 break; 1227 break;
@@ -1122,15 +1230,16 @@ static int call_getopt(int argc, char **argv) {
1122 break; 1230 break;
1123 } 1231 }
1124 } 1232 }
1125 return optind;
1126}
1127 1233
1128static int validate_arguments(int argc) { 1234 if (argc - optind > 0) {
1129
1130 if (argc - optind > 0)
1131 usage(_("Got unexpected non-option argument")); 1235 usage(_("Got unexpected non-option argument"));
1236 }
1132 1237
1133 return OK; 1238 process_arguments_wrapper result = {
1239 .config = config,
1240 .error = OK,
1241 };
1242 return result;
1134} 1243}
1135 1244
1136#if defined(__sun__) || defined(__solaris__) || defined(__hpux__) 1245#if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
@@ -1249,7 +1358,7 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1249 * 1358 *
1250 ***********************************************************************/ 1359 ***********************************************************************/
1251 1360
1252static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { 1361long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1253 int fd; 1362 int fd;
1254 u_char mac_addr[25]; 1363 u_char mac_addr[25];
1255 1364
@@ -1268,51 +1377,53 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1268#endif 1377#endif
1269 1378
1270/* resolve host name or die (TODO: move this to netutils.c!) */ 1379/* resolve host name or die (TODO: move this to netutils.c!) */
1271static void resolve_host(const char *in, struct in_addr *out) { 1380void resolve_host(const char *name, struct in_addr *out) {
1272 struct addrinfo hints, *ai; 1381 struct addrinfo hints = {
1382 .ai_family = PF_INET,
1383 };
1384 struct addrinfo *addr_info;
1273 1385
1274 memset(&hints, 0, sizeof(hints)); 1386 if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) {
1275 hints.ai_family = PF_INET;
1276 if (getaddrinfo(in, NULL, &hints, &ai) != 0)
1277 usage_va(_("Invalid hostname/address - %s"), optarg); 1387 usage_va(_("Invalid hostname/address - %s"), optarg);
1388 }
1278 1389
1279 memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); 1390 memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out));
1280 freeaddrinfo(ai); 1391 freeaddrinfo(addr_info);
1281} 1392}
1282 1393
1283/* parse MAC address string, return 6 bytes (unterminated) or NULL */ 1394/* parse MAC address string, return 6 bytes (unterminated) or NULL */
1284static unsigned char *mac_aton(const char *string) { 1395unsigned char *mac_aton(const char *string) {
1285 static unsigned char result[6]; 1396 static unsigned char result[MAC_ADDR_LEN];
1286 char tmp[3]; 1397 char tmp[3];
1287 unsigned i, j; 1398 unsigned byte_counter = 0;
1288 1399
1289 for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { 1400 for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) {
1290 /* ignore ':' and any other non-hex character */ 1401 /* ignore ':' and any other non-hex character */
1291 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) 1402 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) {
1292 continue; 1403 continue;
1404 }
1293 tmp[0] = string[i]; 1405 tmp[0] = string[i];
1294 tmp[1] = string[i + 1]; 1406 tmp[1] = string[i + 1];
1295 tmp[2] = '\0'; 1407 tmp[2] = '\0';
1296 result[j] = strtol(tmp, (char **)NULL, 16); 1408 result[byte_counter] = strtol(tmp, (char **)NULL, 16);
1297 i++; 1409 i++;
1298 j++; 1410 byte_counter++;
1299 } 1411 }
1300 1412
1301 return (j == 6) ? result : NULL; 1413 return (byte_counter == MAC_ADDR_LEN) ? result : NULL;
1302} 1414}
1303 1415
1304static void print_hardware_address(const unsigned char *address) { 1416void print_hardware_address(const unsigned char *address) {
1305 int i;
1306 1417
1307 printf(_("Hardware address: ")); 1418 printf(_("Hardware address: "));
1308 for (i = 0; i < 5; i++) 1419 for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) {
1309 printf("%2.2x:", address[i]); 1420 printf("%2.2x:", address[addr_idx]);
1310 printf("%2.2x", address[i]); 1421 }
1311 putchar('\n'); 1422 putchar('\n');
1312} 1423}
1313 1424
1314/* print usage help */ 1425/* print usage help */
1315static void print_help(void) { 1426void print_help(void) {
1316 1427
1317 print_revision(progname, NP_VERSION); 1428 print_revision(progname, NP_VERSION);
1318 1429
@@ -1328,6 +1439,7 @@ static void print_help(void) {
1328 printf(UT_HELP_VRSN); 1439 printf(UT_HELP_VRSN);
1329 printf(UT_EXTRA_OPTS); 1440 printf(UT_EXTRA_OPTS);
1330 1441
1442 printf(UT_OUTPUT_FORMAT);
1331 printf(UT_VERBOSE); 1443 printf(UT_VERBOSE);
1332 1444
1333 printf(" %s\n", "-s, --serverip=IPADDRESS"); 1445 printf(" %s\n", "-s, --serverip=IPADDRESS");
@@ -1346,7 +1458,6 @@ static void print_help(void) {
1346 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); 1458 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s"));
1347 1459
1348 printf(UT_SUPPORT); 1460 printf(UT_SUPPORT);
1349 return;
1350} 1461}
1351 1462
1352void print_usage(void) { 1463void print_usage(void) {
@@ -1354,6 +1465,4 @@ void print_usage(void) {
1354 printf("%s\n", _("Usage:")); 1465 printf("%s\n", _("Usage:"));
1355 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); 1466 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname);
1356 printf(" [-i interface] [-m mac]\n"); 1467 printf(" [-i interface] [-m mac]\n");
1357
1358 return;
1359} 1468}
diff --git a/plugins-root/check_dhcp.d/config.h b/plugins-root/check_dhcp.d/config.h
new file mode 100644
index 00000000..f189068b
--- /dev/null
+++ b/plugins-root/check_dhcp.d/config.h
@@ -0,0 +1,50 @@
1#pragma once
2
3#include "../../config.h"
4#include "../lib/states.h"
5#include <stdbool.h>
6#include <netinet/in.h>
7#include "net/if.h"
8#include "output.h"
9
10typedef struct requested_server_struct {
11 struct in_addr server_address;
12 bool answered;
13 struct requested_server_struct *next;
14} requested_server;
15
16typedef struct check_dhcp_config {
17 bool unicast_mode; /* unicast mode: mimic a DHCP relay */
18 bool exclusive_mode; /* exclusive mode aka "rogue DHCP server detection" */
19 int num_of_requested_servers;
20 struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
21 struct in_addr requested_address;
22 bool request_specific_address;
23
24 int dhcpoffer_timeout;
25 unsigned char *user_specified_mac;
26 char network_interface_name[IFNAMSIZ];
27 requested_server *requested_server_list;
28
29 mp_output_format output_format;
30 bool output_format_is_set;
31} check_dhcp_config;
32
33check_dhcp_config check_dhcp_config_init(void) {
34 check_dhcp_config tmp = {
35 .unicast_mode = false,
36 .exclusive_mode = false,
37 .num_of_requested_servers = 0,
38 .dhcp_ip = {0},
39 .requested_address = {0},
40 .request_specific_address = false,
41
42 .dhcpoffer_timeout = 2,
43 .user_specified_mac = NULL,
44 .network_interface_name = "eth0",
45 .requested_server_list = NULL,
46
47 .output_format_is_set = false,
48 };
49 return tmp;
50}
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index dcaceddb..d46d2ccc 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -46,6 +46,8 @@ const char *email = "devel@monitoring-plugins.org";
46#include "../plugins/common.h" 46#include "../plugins/common.h"
47#include "netutils.h" 47#include "netutils.h"
48#include "utils.h" 48#include "utils.h"
49#include "output.h"
50#include "perfdata.h"
49 51
50#if HAVE_SYS_SOCKIO_H 52#if HAVE_SYS_SOCKIO_H
51# include <sys/sockio.h> 53# include <sys/sockio.h>
@@ -65,6 +67,17 @@ const char *email = "devel@monitoring-plugins.org";
65#include <netinet/icmp6.h> 67#include <netinet/icmp6.h>
66#include <arpa/inet.h> 68#include <arpa/inet.h>
67#include <math.h> 69#include <math.h>
70#include <netdb.h>
71#include <sys/types.h>
72#include <unistd.h>
73#include <stdint.h>
74#include <sys/socket.h>
75#include <assert.h>
76#include <sys/select.h>
77
78#include "../lib/states.h"
79#include "./check_icmp.d/config.h"
80#include "./check_icmp.d/check_icmp_helpers.h"
68 81
69/** sometimes undefined system macros (quite a few, actually) **/ 82/** sometimes undefined system macros (quite a few, actually) **/
70#ifndef MAXTTL 83#ifndef MAXTTL
@@ -96,56 +109,8 @@ const char *email = "devel@monitoring-plugins.org";
96# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 109# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
97#endif 110#endif
98 111
99typedef unsigned short range_t; /* type for get_range() -- unimplemented */
100
101typedef struct rta_host {
102 unsigned short id; /* id in **table, and icmp pkts */
103 char *name; /* arg used for adding this host */
104 char *msg; /* icmp error message, if any */
105 struct sockaddr_storage saddr_in; /* the address of this host */
106 struct sockaddr_storage error_addr; /* stores address of error replies */
107 unsigned long long time_waited; /* total time waited, in usecs */
108 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
109 unsigned char icmp_type, icmp_code; /* type and code from errors */
110 unsigned short flags; /* control/status flags */
111 double rta; /* measured RTA */
112 int rta_status; // check result for RTA checks
113 double rtmax; /* max rtt */
114 double rtmin; /* min rtt */
115 double jitter; /* measured jitter */
116 int jitter_status; // check result for Jitter checks
117 double jitter_max; /* jitter rtt maximum */
118 double jitter_min; /* jitter rtt minimum */
119 double EffectiveLatency;
120 double mos; /* Mean opnion score */
121 int mos_status; // check result for MOS checks
122 double score; /* score */
123 int score_status; // check result for score checks
124 u_int last_tdiff;
125 u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
126 unsigned char pl; /* measured packet loss */
127 int pl_status; // check result for packet loss checks
128 struct rta_host *next; /* linked list */
129 int order_status; // check result for packet order checks
130} rta_host;
131
132#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */ 112#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
133 113
134/* threshold structure. all values are maximum allowed, exclusive */
135typedef struct threshold {
136 unsigned char pl; /* max allowed packet loss in percent */
137 unsigned int rta; /* roundtrip time average, microseconds */
138 double jitter; /* jitter time average, microseconds */
139 double mos; /* MOS */
140 double score; /* Score */
141} threshold;
142
143/* the data structure */
144typedef struct icmp_ping_data {
145 struct timeval stime; /* timestamp (saved in protocol struct as well) */
146 unsigned short ping_id;
147} icmp_ping_data;
148
149typedef union ip_hdr { 114typedef union ip_hdr {
150 struct ip ip; 115 struct ip ip;
151 struct ip6_hdr ip6; 116 struct ip6_hdr ip6;
@@ -158,24 +123,6 @@ typedef union icmp_packet {
158 u_short *cksum_in; 123 u_short *cksum_in;
159} icmp_packet; 124} icmp_packet;
160 125
161/* the different modes of this program are as follows:
162 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
163 * MODE_HOSTCHECK: Return immediately upon any sign of life
164 * In addition, sends packets to ALL addresses assigned
165 * to this host (as returned by gethostbyname() or
166 * gethostbyaddr() and expects one host only to be checked at
167 * a time. Therefore, any packet response what so ever will
168 * count as a sign of life, even when received outside
169 * crit.rta limit. Do not misspell any additional IP's.
170 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
171 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
172 * tcp and udp args does this)
173 */
174#define MODE_RTA 0
175#define MODE_HOSTCHECK 1
176#define MODE_ALL 2
177#define MODE_ICMP 3
178
179enum enum_threshold_mode { 126enum enum_threshold_mode {
180 const_rta_mode, 127 const_rta_mode,
181 const_packet_loss_mode, 128 const_packet_loss_mode,
@@ -186,89 +133,487 @@ enum enum_threshold_mode {
186 133
187typedef enum enum_threshold_mode threshold_mode; 134typedef enum enum_threshold_mode threshold_mode;
188 135
189/* the different ping types we can do
190 * TODO: investigate ARP ping as well */
191#define HAVE_ICMP 1
192#define HAVE_UDP 2
193#define HAVE_TCP 4
194#define HAVE_ARP 8
195
196#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
197#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
198#define IP_HDR_SIZE 20
199#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
200#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
201
202/* various target states */
203#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
204#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
205#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
206#define TSTATE_UNREACH 0x08
207
208/** prototypes **/ 136/** prototypes **/
209void print_help(void); 137void print_help();
210void print_usage(void); 138void print_usage(void);
211static u_int get_timevar(const char *); 139
212static u_int get_timevaldiff(struct timeval *, struct timeval *); 140/* Time related */
213static in_addr_t get_ip_address(const char *); 141typedef struct {
214static int wait_for_reply(int, u_int); 142 int error_code;
215static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); 143 time_t time_range;
216static int send_icmp_ping(int, struct rta_host *); 144} get_timevar_wrapper;
217static int get_threshold(char *str, threshold *th); 145static get_timevar_wrapper get_timevar(const char *str);
218static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); 146static time_t get_timevaldiff(struct timeval earlier, struct timeval later);
219static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); 147static time_t get_timevaldiff_to_now(struct timeval earlier);
220static void run_checks(void); 148
221static void set_source_ip(char *); 149static in_addr_t get_ip_address(const char *ifname);
222static int add_target(char *); 150static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family);
223static int add_target_ip(char *, struct sockaddr_storage *); 151
224static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); 152/* Receiving data */
225static void parse_address(struct sockaddr_storage *, char *, int); 153static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval,
226static unsigned short icmp_checksum(uint16_t *, size_t); 154 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
227static void finish(int); 155 ping_target **table, unsigned short packets,
228static void crash(const char *, ...); 156 unsigned short number_of_targets, check_icmp_state *program_state);
229 157
230/** external **/ 158typedef struct {
231extern int optind; 159 sa_family_t recv_proto;
232extern char *optarg; 160 ssize_t received;
233extern char **environ; 161} recvfrom_wto_wrapper;
162static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *buf, unsigned int len,
163 struct sockaddr *saddr, time_t *timeout,
164 struct timeval *received_timestamp);
165static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
166 time_t *target_interval, uint16_t sender_id, ping_target **table,
167 unsigned short packets, unsigned short number_of_targets,
168 check_icmp_state *program_state);
169
170/* Sending data */
171static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host,
172 unsigned short icmp_pkt_size, uint16_t sender_id,
173 check_icmp_state *program_state);
174
175/* Threshold related */
176typedef struct {
177 int errorcode;
178 check_icmp_threshold threshold;
179} get_threshold_wrapper;
180static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold);
181
182typedef struct {
183 int errorcode;
184 check_icmp_threshold warn;
185 check_icmp_threshold crit;
186} get_threshold2_wrapper;
187static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
188 check_icmp_threshold crit, threshold_mode mode);
189
190typedef struct {
191 int errorcode;
192 check_icmp_threshold result;
193} parse_threshold2_helper_wrapper;
194static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
195 size_t length,
196 check_icmp_threshold thr,
197 threshold_mode mode);
198
199/* main test function */
200static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
201 check_icmp_execution_mode mode, time_t max_completion_time,
202 struct timeval prog_start, ping_target **table, unsigned short packets,
203 check_icmp_socket_set sockset, unsigned short number_of_targets,
204 check_icmp_state *program_state);
205mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
206 check_icmp_threshold warn, check_icmp_threshold crit);
207
208typedef struct {
209 int targets_ok;
210 int targets_warn;
211 mp_subcheck sc_host;
212} evaluate_host_wrapper;
213evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
214 check_icmp_mode_switches modes, check_icmp_threshold warn,
215 check_icmp_threshold crit);
216
217/* Target acquisition */
218typedef struct {
219 int error_code;
220 check_icmp_target_container host;
221 bool has_v4;
222 bool has_v6;
223} add_host_wrapper;
224static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
225 sa_family_t enforced_proto);
226
227typedef struct {
228 int error_code;
229 ping_target *targets;
230 unsigned int number_of_targets;
231 bool has_v4;
232 bool has_v6;
233} add_target_wrapper;
234static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode,
235 sa_family_t enforced_proto);
236
237typedef struct {
238 int error_code;
239 ping_target *target;
240} add_target_ip_wrapper;
241static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address);
242
243static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size);
244
245static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size);
246
247/* End of run function */
248static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive,
249 check_icmp_threshold warn, check_icmp_threshold crit,
250 unsigned short number_of_targets, check_icmp_state *program_state,
251 check_icmp_target_container host_list[], unsigned short number_of_hosts,
252 mp_check overall[static 1]);
253
254/* Error exit */
255static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
234 256
235/** global variables **/ 257/** global variables **/
236static struct rta_host **table, *cursor, *list; 258static int debug = 0;
237 259
238static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 260extern unsigned int timeout;
239static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 261
240 262/** the working code **/
241static int mode, protocols, sockets, debug = 0, timeout = 10; 263static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) {
242static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; 264 return targets - targets_down;
243static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; 265}
244 266static inline unsigned int icmp_pkts_en_route(unsigned int icmp_sent, unsigned int icmp_recv,
245static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0; 267 unsigned int icmp_lost) {
246#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) 268 return icmp_sent - (icmp_recv + icmp_lost);
247static unsigned short targets_down = 0, targets = 0, packets = 0; 269}
248#define targets_alive (targets - targets_down) 270
249static unsigned int retry_interval, pkt_interval, target_interval; 271// Create configuration from cli parameters
250static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; 272typedef struct {
251static pid_t pid; 273 int errorcode;
252static struct timezone tz; 274 check_icmp_config config;
253static struct timeval prog_start; 275} check_icmp_config_wrapper;
254static unsigned long long max_completion_time = 0; 276check_icmp_config_wrapper process_arguments(int argc, char **argv) {
255static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ 277 /* get calling name the old-fashioned way for portability instead
256static int min_hosts_alive = -1; 278 * of relying on the glibc-ism __progname */
257static float pkt_backoff_factor = 1.5; 279 char *ptr = strrchr(argv[0], '/');
258static float target_backoff_factor = 1.5; 280 if (ptr) {
259static bool rta_mode = false; 281 progname = &ptr[1];
260static bool pl_mode = false; 282 } else {
261static bool jitter_mode = false; 283 progname = argv[0];
262static bool score_mode = false; 284 }
263static bool mos_mode = false; 285
264static bool order_mode = false; 286 check_icmp_config_wrapper result = {
287 .errorcode = OK,
288 .config = check_icmp_config_init(),
289 };
290
291 /* use the pid to mark packets as ours */
292 /* Some systems have 32-bit pid_t so mask off only 16 bits */
293 result.config.sender_id = getpid() & 0xffff;
294
295 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
296 result.config.mode = MODE_ICMP;
297 } else if (!strcmp(progname, "check_host")) {
298 result.config.mode = MODE_HOSTCHECK;
299 result.config.number_of_packets = 5;
300 result.config.crit.rta = result.config.warn.rta = 1000000;
301 result.config.crit.pl = result.config.warn.pl = 100;
302 } else if (!strcmp(progname, "check_rta_multi")) {
303 result.config.mode = MODE_ALL;
304 result.config.target_interval = 0;
305 result.config.number_of_packets = 5;
306 }
307 /* support "--help" and "--version" */
308 if (argc == 2) {
309 if (!strcmp(argv[1], "--help")) {
310 strcpy(argv[1], "-h");
311 }
312 if (!strcmp(argv[1], "--version")) {
313 strcpy(argv[1], "-V");
314 }
315 }
316
317 sa_family_t enforced_ai_family = AF_UNSPEC;
318
319 enum {
320 output_format_index = CHAR_MAX + 1,
321 };
322
323 struct option longopts[] = {
324 {"version", no_argument, 0, 'V'},
325 {"help", no_argument, 0, 'h'},
326 {"verbose", no_argument, 0, 'v'},
327 {"Host", required_argument, 0, 'H'},
328 {"ipv4-only", no_argument, 0, '4'},
329 {"ipv6-only", no_argument, 0, '6'},
330 {"warning", required_argument, 0, 'w'},
331 {"critical", required_argument, 0, 'c'},
332 {"rta-mode-thresholds", required_argument, 0, 'R'},
333 {"packet-loss-mode-thresholds", required_argument, 0, 'P'},
334 {"jitter-mode-thresholds", required_argument, 0, 'J'},
335 {"mos-mode-thresholds", required_argument, 0, 'M'},
336 {"score-mode-thresholds", required_argument, 0, 'S'},
337 {"out-of-order-packets", no_argument, 0, 'O'},
338 {"number-of-packets", required_argument, 0, 'n'},
339 {"number-of-packets", required_argument, 0, 'p'},
340 {"packet-interval", required_argument, 0, 'i'},
341 {"target-interval", required_argument, 0, 'I'},
342 {"minimal-host-alive", required_argument, 0, 'm'},
343 {"outgoing-ttl", required_argument, 0, 'l'},
344 {"size", required_argument, 0, 'b'},
345 {"output-format", required_argument, 0, output_format_index},
346 {},
347 };
348
349 // Parse protocol arguments first
350 // and count hosts here
351 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
352 for (int i = 1; i < argc; i++) {
353 long int arg;
354 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
355 switch (arg) {
356
357 case '4':
358 if (enforced_ai_family != AF_UNSPEC) {
359 crash("Multiple protocol versions not supported");
360 }
361 enforced_ai_family = AF_INET;
362 break;
363 case '6':
364 if (enforced_ai_family != AF_UNSPEC) {
365 crash("Multiple protocol versions not supported");
366 }
367 enforced_ai_family = AF_INET6;
368 break;
369 case 'H': {
370 result.config.number_of_hosts++;
371 break;
372 }
373 case 'h': /* help */
374 // Trigger help here to avoid adding hosts before that (and doing DNS queries)
375 print_help();
376 exit(STATE_UNKNOWN);
377 break;
378 case 'v':
379 debug++;
380 break;
381 }
382 }
383 }
384
385 char **tmp = &argv[optind];
386 while (*tmp) {
387 result.config.number_of_hosts++;
388 tmp++;
389 }
390
391 // Sanity check: if hostmode is selected,only a single host is allowed
392 if (result.config.mode == MODE_HOSTCHECK && result.config.number_of_hosts > 1) {
393 usage("check_host only allows a single host");
394 }
395
396 // Allocate hosts
397 result.config.hosts =
398 calloc(result.config.number_of_hosts, sizeof(check_icmp_target_container));
399 if (result.config.hosts == NULL) {
400 crash("failed to allocate memory");
401 }
402
403 /* Reset argument scanning */
404 optind = 1;
405
406 int host_counter = 0;
407 /* parse the arguments */
408 for (int i = 1; i < argc; i++) {
409 long int arg;
410 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
411 switch (arg) {
412 case 'b': {
413 long size = strtol(optarg, NULL, 0);
414 if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
415 size < MAX_PING_DATA) {
416 result.config.icmp_data_size = (unsigned short)size;
417 } else {
418 usage_va("ICMP data length must be between: %lu and %lu",
419 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
420 MAX_PING_DATA - 1);
421 }
422 } break;
423 case 'i': {
424 // packet_interval was unused and is now removed
425 } break;
426 case 'I': {
427 get_timevar_wrapper parsed_time = get_timevar(optarg);
428
429 if (parsed_time.error_code == OK) {
430 result.config.target_interval = parsed_time.time_range;
431 } else {
432 crash("failed to parse target interval");
433 }
434 } break;
435 case 'w': {
436 get_threshold_wrapper warn = get_threshold(optarg, result.config.warn);
437 if (warn.errorcode == OK) {
438 result.config.warn = warn.threshold;
439 } else {
440 crash("failed to parse warning threshold");
441 }
442 } break;
443 case 'c': {
444 get_threshold_wrapper crit = get_threshold(optarg, result.config.crit);
445 if (crit.errorcode == OK) {
446 result.config.crit = crit.threshold;
447 } else {
448 crash("failed to parse critical threshold");
449 }
450 } break;
451 case 'n':
452 case 'p':
453 result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0);
454 if (result.config.number_of_packets > 20) {
455 errno = 0;
456 crash("packets is > 20 (%d)", result.config.number_of_packets);
457 }
458 break;
459 case 't':
460 // WARNING Deprecated since execution time is determined by the other factors
461 break;
462 case 'H': {
463 add_host_wrapper host_add_result =
464 add_host(optarg, result.config.mode, enforced_ai_family);
465 if (host_add_result.error_code == OK) {
466 result.config.hosts[host_counter] = host_add_result.host;
467 host_counter++;
468
469 if (result.config.targets != NULL) {
470 result.config.number_of_targets += ping_target_list_append(
471 result.config.targets, host_add_result.host.target_list);
472 } else {
473 result.config.targets = host_add_result.host.target_list;
474 result.config.number_of_targets += host_add_result.host.number_of_targets;
475 }
476
477 if (host_add_result.has_v4) {
478 result.config.need_v4 = true;
479 }
480 if (host_add_result.has_v6) {
481 result.config.need_v6 = true;
482 }
483 } else {
484 crash("Failed to add host, unable to parse it correctly");
485 }
486 } break;
487 case 'l':
488 result.config.ttl = strtoul(optarg, NULL, 0);
489 break;
490 case 'm':
491 result.config.min_hosts_alive = (int)strtoul(optarg, NULL, 0);
492 break;
493 case 's': /* specify source IP address */
494 result.config.source_ip = optarg;
495 break;
496 case 'V': /* version */
497 print_revision(progname, NP_VERSION);
498 exit(STATE_UNKNOWN);
499 case 'R': /* RTA mode */ {
500 get_threshold2_wrapper rta_th = get_threshold2(
501 optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode);
502
503 if (rta_th.errorcode != OK) {
504 crash("Failed to parse RTA threshold");
505 }
506
507 result.config.warn = rta_th.warn;
508 result.config.crit = rta_th.crit;
509 result.config.modes.rta_mode = true;
510 } break;
511 case 'P': /* packet loss mode */ {
512 get_threshold2_wrapper pl_th =
513 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
514 const_packet_loss_mode);
515 if (pl_th.errorcode != OK) {
516 crash("Failed to parse packet loss threshold");
517 }
518
519 result.config.warn = pl_th.warn;
520 result.config.crit = pl_th.crit;
521 result.config.modes.pl_mode = true;
522 } break;
523 case 'J': /* jitter mode */ {
524 get_threshold2_wrapper jitter_th =
525 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
526 const_jitter_mode);
527 if (jitter_th.errorcode != OK) {
528 crash("Failed to parse jitter threshold");
529 }
530
531 result.config.warn = jitter_th.warn;
532 result.config.crit = jitter_th.crit;
533 result.config.modes.jitter_mode = true;
534 } break;
535 case 'M': /* MOS mode */ {
536 get_threshold2_wrapper mos_th = get_threshold2(
537 optarg, strlen(optarg), result.config.warn, result.config.crit, const_mos_mode);
538 if (mos_th.errorcode != OK) {
539 crash("Failed to parse MOS threshold");
540 }
541
542 result.config.warn = mos_th.warn;
543 result.config.crit = mos_th.crit;
544 result.config.modes.mos_mode = true;
545 } break;
546 case 'S': /* score mode */ {
547 get_threshold2_wrapper score_th =
548 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
549 const_score_mode);
550 if (score_th.errorcode != OK) {
551 crash("Failed to parse score threshold");
552 }
553
554 result.config.warn = score_th.warn;
555 result.config.crit = score_th.crit;
556 result.config.modes.score_mode = true;
557 } break;
558 case 'O': /* out of order mode */
559 result.config.modes.order_mode = true;
560 break;
561 case output_format_index: {
562 parsed_output_format parser = mp_parse_output_format(optarg);
563 if (!parser.parsing_success) {
564 // TODO List all available formats here, maybe add anothoer usage function
565 printf("Invalid output format: %s\n", optarg);
566 exit(STATE_UNKNOWN);
567 }
568
569 result.config.output_format_is_set = true;
570 result.config.output_format = parser.output_format;
571 break;
572 }
573 }
574 }
575 }
576
577 argv = &argv[optind];
578 while (*argv) {
579 add_target(*argv, result.config.mode, enforced_ai_family);
580 argv++;
581 }
582
583 if (!result.config.number_of_targets) {
584 errno = 0;
585 crash("No hosts to check");
586 }
587
588 /* stupid users should be able to give whatever thresholds they want
589 * (nothing will break if they do), but some anal plugin maintainer
590 * will probably add some printf() thing here later, so it might be
591 * best to at least show them where to do it. ;) */
592 if (result.config.warn.pl > result.config.crit.pl) {
593 result.config.warn.pl = result.config.crit.pl;
594 }
595 if (result.config.warn.rta > result.config.crit.rta) {
596 result.config.warn.rta = result.config.crit.rta;
597 }
598 if (result.config.warn.jitter > result.config.crit.jitter) {
599 result.config.crit.jitter = result.config.warn.jitter;
600 }
601 if (result.config.warn.mos < result.config.crit.mos) {
602 result.config.warn.mos = result.config.crit.mos;
603 }
604 if (result.config.warn.score < result.config.crit.score) {
605 result.config.warn.score = result.config.crit.score;
606 }
607
608 return result;
609}
265 610
266/** code start **/ 611/** code start **/
267static void crash(const char *fmt, ...) { 612static void crash(const char *fmt, ...) {
268 va_list ap;
269 613
270 printf("%s: ", progname); 614 printf("%s: ", progname);
271 615
616 va_list ap;
272 va_start(ap, fmt); 617 va_start(ap, fmt);
273 vprintf(fmt, ap); 618 vprintf(fmt, ap);
274 va_end(ap); 619 va_end(ap);
@@ -385,18 +730,20 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm
385 return msg; 730 return msg;
386} 731}
387 732
388static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { 733static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
389 struct icmp p, sent_icmp; 734 time_t *target_interval, const uint16_t sender_id,
390 struct rta_host *host = NULL; 735 ping_target **table, unsigned short packets,
391 736 const unsigned short number_of_targets,
392 memcpy(&p, packet, sizeof(p)); 737 check_icmp_state *program_state) {
393 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { 738 struct icmp icmp_packet;
739 memcpy(&icmp_packet, packet, sizeof(icmp_packet));
740 if (icmp_packet.icmp_type == ICMP_ECHO && ntohs(icmp_packet.icmp_id) == sender_id) {
394 /* echo request from us to us (pinging localhost) */ 741 /* echo request from us to us (pinging localhost) */
395 return 0; 742 return 0;
396 } 743 }
397 744
398 if (debug) { 745 if (debug) {
399 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); 746 printf("handle_random_icmp(%p, %p)\n", (void *)&icmp_packet, (void *)addr);
400 } 747 }
401 748
402 /* only handle a few types, since others can't possibly be replies to 749 /* only handle a few types, since others can't possibly be replies to
@@ -409,14 +756,17 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
409 * TIMXCEED actually sends a proper icmp response we will have passed 756 * TIMXCEED actually sends a proper icmp response we will have passed
410 * too many hops to have a hope of reaching it later, in which case it 757 * too many hops to have a hope of reaching it later, in which case it
411 * indicates overconfidence in the network, poor routing or both. */ 758 * indicates overconfidence in the network, poor routing or both. */
412 if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { 759 if (icmp_packet.icmp_type != ICMP_UNREACH && icmp_packet.icmp_type != ICMP_TIMXCEED &&
760 icmp_packet.icmp_type != ICMP_SOURCEQUENCH && icmp_packet.icmp_type != ICMP_PARAMPROB) {
413 return 0; 761 return 0;
414 } 762 }
415 763
416 /* might be for us. At least it holds the original package (according 764 /* might be for us. At least it holds the original package (according
417 * to RFC 792). If it isn't, just ignore it */ 765 * to RFC 792). If it isn't, just ignore it */
766 struct icmp sent_icmp;
418 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); 767 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
419 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) { 768 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != sender_id ||
769 ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) {
420 if (debug) { 770 if (debug) {
421 printf("Packet is no response to a packet we sent\n"); 771 printf("Packet is no response to a packet we sent\n");
422 } 772 }
@@ -424,14 +774,15 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
424 } 774 }
425 775
426 /* it is indeed a response for us */ 776 /* it is indeed a response for us */
427 host = table[ntohs(sent_icmp.icmp_seq) / packets]; 777 ping_target *host = table[ntohs(sent_icmp.icmp_seq) / packets];
428 if (debug) { 778 if (debug) {
429 char address[INET6_ADDRSTRLEN]; 779 char address[INET6_ADDRSTRLEN];
430 parse_address(addr, address, sizeof(address)); 780 parse_address(addr, address, sizeof(address));
431 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); 781 printf("Received \"%s\" from %s for ICMP ECHO sent.\n",
782 get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address);
432 } 783 }
433 784
434 icmp_lost++; 785 program_state->icmp_lost++;
435 host->icmp_lost++; 786 host->icmp_lost++;
436 /* don't spend time on lost hosts any more */ 787 /* don't spend time on lost hosts any more */
437 if (host->flags & FLAG_LOST_CAUSE) { 788 if (host->flags & FLAG_LOST_CAUSE) {
@@ -440,305 +791,104 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
440 791
441 /* source quench means we're sending too fast, so increase the 792 /* source quench means we're sending too fast, so increase the
442 * interval and mark this packet lost */ 793 * interval and mark this packet lost */
443 if (p.icmp_type == ICMP_SOURCEQUENCH) { 794 if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) {
444 pkt_interval *= pkt_backoff_factor; 795 *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR);
445 target_interval *= target_backoff_factor;
446 } else { 796 } else {
447 targets_down++; 797 program_state->targets_down++;
448 host->flags |= FLAG_LOST_CAUSE; 798 host->flags |= FLAG_LOST_CAUSE;
449 } 799 }
450 host->icmp_type = p.icmp_type; 800 host->icmp_type = icmp_packet.icmp_type;
451 host->icmp_code = p.icmp_code; 801 host->icmp_code = icmp_packet.icmp_code;
452 host->error_addr = *addr; 802 host->error_addr = *addr;
453 803
454 return 0; 804 return 0;
455} 805}
456 806
457void parse_address(struct sockaddr_storage *addr, char *address, int size) { 807void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size) {
458 switch (address_family) { 808 switch (addr->ss_family) {
459 case AF_INET: 809 case AF_INET:
460 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); 810 inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, dst, size);
461 break; 811 break;
462 case AF_INET6: 812 case AF_INET6:
463 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); 813 inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, dst, size);
464 break; 814 break;
815 default:
816 assert(false);
465 } 817 }
466} 818}
467 819
468int main(int argc, char **argv) { 820int main(int argc, char **argv) {
469 int i;
470 char *ptr;
471 long int arg;
472 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
473 int result;
474 struct rta_host *host;
475#ifdef HAVE_SIGACTION
476 struct sigaction sig_action;
477#endif
478#ifdef SO_TIMESTAMP
479 int on = 1;
480#endif
481 char *source_ip = NULL;
482 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
483 setlocale(LC_ALL, ""); 821 setlocale(LC_ALL, "");
484 bindtextdomain(PACKAGE, LOCALEDIR); 822 bindtextdomain(PACKAGE, LOCALEDIR);
485 textdomain(PACKAGE); 823 textdomain(PACKAGE);
486 824
487 /* we only need to be setsuid when we get the sockets, so do 825 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
488 * that before pointer magic (esp. on network data) */ 826 environ = NULL;
489 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
490
491 address_family = -1;
492 int icmp_proto = IPPROTO_ICMP;
493 827
494 /* get calling name the old-fashioned way for portability instead 828 /* Parse extra opts if any */
495 * of relying on the glibc-ism __progname */ 829 argv = np_extra_opts(&argc, argv, progname);
496 ptr = strrchr(argv[0], '/');
497 if (ptr) {
498 progname = &ptr[1];
499 } else {
500 progname = argv[0];
501 }
502 830
503 /* now set defaults. Use progname to set them initially (allows for 831 check_icmp_config_wrapper tmp_config = process_arguments(argc, argv);
504 * superfast check_host program when target host is up */
505 cursor = list = NULL;
506 table = NULL;
507
508 mode = MODE_RTA;
509 /* Default critical thresholds */
510 crit.rta = 500000;
511 crit.pl = 80;
512 crit.jitter = 50;
513 crit.mos = 3;
514 crit.score = 70;
515 /* Default warning thresholds */
516 warn.rta = 200000;
517 warn.pl = 40;
518 warn.jitter = 40;
519 warn.mos = 3.5;
520 warn.score = 80;
521
522 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
523 pkt_interval = 80000; /* 80 msec packet interval by default */
524 packets = 5;
525 832
526 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { 833 if (tmp_config.errorcode != OK) {
527 mode = MODE_ICMP; 834 crash("failed to parse config");
528 protocols = HAVE_ICMP;
529 } else if (!strcmp(progname, "check_host")) {
530 mode = MODE_HOSTCHECK;
531 pkt_interval = 1000000;
532 packets = 5;
533 crit.rta = warn.rta = 1000000;
534 crit.pl = warn.pl = 100;
535 } else if (!strcmp(progname, "check_rta_multi")) {
536 mode = MODE_ALL;
537 target_interval = 0;
538 pkt_interval = 50000;
539 packets = 5;
540 } 835 }
541 836
542 /* support "--help" and "--version" */ 837 const check_icmp_config config = tmp_config.config;
543 if (argc == 2) {
544 if (!strcmp(argv[1], "--help")) {
545 strcpy(argv[1], "-h");
546 }
547 if (!strcmp(argv[1], "--version")) {
548 strcpy(argv[1], "-V");
549 }
550 }
551 838
552 /* Parse protocol arguments first */ 839 if (config.output_format_is_set) {
553 for (i = 1; i < argc; i++) { 840 mp_set_format(config.output_format);
554 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
555 switch (arg) {
556 case '4':
557 if (address_family != -1) {
558 crash("Multiple protocol versions not supported");
559 }
560 address_family = AF_INET;
561 break;
562 case '6':
563#ifdef USE_IPV6
564 if (address_family != -1) {
565 crash("Multiple protocol versions not supported");
566 }
567 address_family = AF_INET6;
568#else
569 usage(_("IPv6 support not available\n"));
570#endif
571 break;
572 }
573 }
574 } 841 }
575 842
576 /* Reset argument scanning */ 843 check_icmp_socket_set sockset = {
577 optind = 1; 844 .socket4 = -1,
845 .socket6 = -1,
846 };
578 847
579 unsigned long size; 848 if (config.need_v4) {
580 bool err; 849 sockset.socket4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
581 /* parse the arguments */ 850 if (sockset.socket4 == -1) {
582 for (i = 1; i < argc; i++) { 851 crash("Failed to obtain ICMP v4 socket");
583 while ((arg = getopt(argc, argv, opts_str)) != EOF) { 852 }
584 switch (arg) {
585 case 'v':
586 debug++;
587 break;
588 case 'b':
589 size = strtol(optarg, NULL, 0);
590 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) {
591 icmp_data_size = size;
592 icmp_pkt_size = size + ICMP_MINLEN;
593 } else {
594 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data),
595 MAX_PING_DATA - 1);
596 }
597 break;
598 case 'i':
599 pkt_interval = get_timevar(optarg);
600 break;
601 case 'I':
602 target_interval = get_timevar(optarg);
603 break;
604 case 'w':
605 get_threshold(optarg, &warn);
606 break;
607 case 'c':
608 get_threshold(optarg, &crit);
609 break;
610 case 'n':
611 case 'p':
612 packets = strtoul(optarg, NULL, 0);
613 break;
614 case 't':
615 timeout = strtoul(optarg, NULL, 0);
616 if (!timeout) {
617 timeout = 10;
618 }
619 break;
620 case 'H':
621 add_target(optarg);
622 break;
623 case 'l':
624 ttl = (int)strtoul(optarg, NULL, 0);
625 break;
626 case 'm':
627 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
628 break;
629 case 'd': /* implement later, for cluster checks */
630 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
631 if (ptr) {
632 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
633 }
634 break;
635 case 's': /* specify source IP address */
636 source_ip = optarg;
637 break;
638 case 'V': /* version */
639 print_revision(progname, NP_VERSION);
640 exit(STATE_UNKNOWN);
641 case 'h': /* help */
642 print_help();
643 exit(STATE_UNKNOWN);
644 break;
645 case 'R': /* RTA mode */
646 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
647 if (!err) {
648 crash("Failed to parse RTA threshold");
649 }
650 853
651 rta_mode = true; 854 if (config.source_ip) {
652 break;
653 case 'P': /* packet loss mode */
654 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
655 if (!err) {
656 crash("Failed to parse packet loss threshold");
657 }
658 855
659 pl_mode = true; 856 struct in_addr tmp = {};
660 break; 857 int error_code = inet_pton(AF_INET, config.source_ip, &tmp);
661 case 'J': /* jitter mode */ 858 if (error_code == 1) {
662 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode); 859 set_source_ip(config.source_ip, sockset.socket4, AF_INET);
663 if (!err) { 860 } else {
664 crash("Failed to parse jitter threshold"); 861 // just try this mindlessly if it's not a v4 address
665 } 862 set_source_ip(config.source_ip, sockset.socket6, AF_INET6);
863 }
864 }
666 865
667 jitter_mode = true; 866#ifdef SO_TIMESTAMP
668 break; 867 if (sockset.socket4 != -1) {
669 case 'M': /* MOS mode */ 868 int on = 1;
670 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode); 869 if (setsockopt(sockset.socket4, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
671 if (!err) { 870 if (debug) {
672 crash("Failed to parse MOS threshold"); 871 printf("Warning: no SO_TIMESTAMP support\n");
673 } 872 }
674 873 }
675 mos_mode = true; 874 }
676 break; 875 if (sockset.socket6 != -1) {
677 case 'S': /* score mode */ 876 int on = 1;
678 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode); 877 if (setsockopt(sockset.socket6, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
679 if (!err) { 878 if (debug) {
680 crash("Failed to parse score threshold"); 879 printf("Warning: no SO_TIMESTAMP support\n");
681 } 880 }
682
683 score_mode = true;
684 break;
685 case 'O': /* out of order mode */
686 order_mode = true;
687 break;
688 } 881 }
689 } 882 }
883#endif // SO_TIMESTAMP
690 } 884 }
691 885
692 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ 886 if (config.need_v6) {
693 environ = NULL; 887 sockset.socket6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
694 888 if (sockset.socket6 == -1) {
695 /* use the pid to mark packets as ours */ 889 crash("Failed to obtain ICMP v6 socket");
696 /* Some systems have 32-bit pid_t so mask off only 16 bits */
697 pid = getpid() & 0xffff;
698 /* printf("pid = %u\n", pid); */
699
700 /* Parse extra opts if any */
701 argv = np_extra_opts(&argc, argv, progname);
702
703 argv = &argv[optind];
704 while (*argv) {
705 add_target(*argv);
706 argv++;
707 }
708
709 if (!targets) {
710 errno = 0;
711 crash("No hosts to check");
712 }
713
714 // add_target might change address_family
715 switch (address_family) {
716 case AF_INET:
717 icmp_proto = IPPROTO_ICMP;
718 break;
719 case AF_INET6:
720 icmp_proto = IPPROTO_ICMPV6;
721 break;
722 default:
723 crash("Address family not supported");
724 }
725 if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1) {
726 sockets |= HAVE_ICMP;
727 } else {
728 icmp_sockerrno = errno;
729 }
730
731 if (source_ip) {
732 set_source_ip(source_ip);
733 }
734
735#ifdef SO_TIMESTAMP
736 if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
737 if (debug) {
738 printf("Warning: no SO_TIMESTAMP support\n");
739 } 890 }
740 } 891 }
741#endif // SO_TIMESTAMP
742 892
743 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ 893 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
744 if (setuid(getuid()) == -1) { 894 if (setuid(getuid()) == -1) {
@@ -746,186 +896,179 @@ int main(int argc, char **argv) {
746 return 1; 896 return 1;
747 } 897 }
748 898
749 if (!sockets) { 899 if (sockset.socket4) {
750 if (icmp_sock == -1) { 900 int result = setsockopt(sockset.socket4, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
751 errno = icmp_sockerrno;
752 crash("Failed to obtain ICMP socket");
753 return -1;
754 }
755 /* if(udp_sock == -1) { */
756 /* errno = icmp_sockerrno; */
757 /* crash("Failed to obtain UDP socket"); */
758 /* return -1; */
759 /* } */
760 /* if(tcp_sock == -1) { */
761 /* errno = icmp_sockerrno; */
762 /* crash("Failed to obtain TCP socker"); */
763 /* return -1; */
764 /* } */
765 }
766 if (!ttl) {
767 ttl = 64;
768 }
769
770 if (icmp_sock) {
771 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
772 if (debug) { 901 if (debug) {
773 if (result == -1) { 902 if (result == -1) {
774 printf("setsockopt failed\n"); 903 printf("setsockopt failed\n");
775 } else { 904 } else {
776 printf("ttl set to %u\n", ttl); 905 printf("ttl set to %lu\n", config.ttl);
777 } 906 }
778 } 907 }
779 } 908 }
780 909
781 /* stupid users should be able to give whatever thresholds they want 910 if (sockset.socket6) {
782 * (nothing will break if they do), but some anal plugin maintainer 911 int result = setsockopt(sockset.socket6, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
783 * will probably add some printf() thing here later, so it might be 912 if (debug) {
784 * best to at least show them where to do it. ;) */ 913 if (result == -1) {
785 if (warn.pl > crit.pl) { 914 printf("setsockopt failed\n");
786 warn.pl = crit.pl; 915 } else {
787 } 916 printf("ttl set to %lu\n", config.ttl);
788 if (warn.rta > crit.rta) { 917 }
789 warn.rta = crit.rta; 918 }
790 }
791 if (warn_down > crit_down) {
792 crit_down = warn_down;
793 }
794 if (warn.jitter > crit.jitter) {
795 crit.jitter = warn.jitter;
796 }
797 if (warn.mos < crit.mos) {
798 warn.mos = crit.mos;
799 }
800 if (warn.score < crit.score) {
801 warn.score = crit.score;
802 }
803
804#ifdef HAVE_SIGACTION
805 sig_action.sa_sigaction = NULL;
806 sig_action.sa_handler = finish;
807 sigfillset(&sig_action.sa_mask);
808 sig_action.sa_flags = SA_NODEFER | SA_RESTART;
809 sigaction(SIGINT, &sig_action, NULL);
810 sigaction(SIGHUP, &sig_action, NULL);
811 sigaction(SIGTERM, &sig_action, NULL);
812 sigaction(SIGALRM, &sig_action, NULL);
813#else /* HAVE_SIGACTION */
814 signal(SIGINT, finish);
815 signal(SIGHUP, finish);
816 signal(SIGTERM, finish);
817 signal(SIGALRM, finish);
818#endif /* HAVE_SIGACTION */
819 if (debug) {
820 printf("Setting alarm timeout to %u seconds\n", timeout);
821 } 919 }
822 alarm(timeout);
823 920
824 /* make sure we don't wait any longer than necessary */ 921 /* make sure we don't wait any longer than necessary */
825 gettimeofday(&prog_start, &tz); 922 struct timeval prog_start;
826 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta; 923 gettimeofday(&prog_start, NULL);
924
925 time_t max_completion_time =
926 (config.target_interval * config.number_of_targets) +
927 (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta;
827 928
828 if (debug) { 929 if (debug) {
829 printf("packets: %u, targets: %u\n" 930 printf("packets: %u, targets: %u\n"
830 "target_interval: %0.3f, pkt_interval %0.3f\n" 931 "target_interval: %0.3f\n"
831 "crit.rta: %0.3f\n" 932 "crit.rta: %0.3f\n"
832 "max_completion_time: %0.3f\n", 933 "max_completion_time: %0.3f\n",
833 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, 934 config.number_of_packets, config.number_of_targets,
935 (float)config.target_interval / 1000, (float)config.crit.rta / 1000,
834 (float)max_completion_time / 1000); 936 (float)max_completion_time / 1000);
835 } 937 }
836 938
837 if (debug) { 939 if (debug) {
838 if (max_completion_time > (u_int)timeout * 1000000) { 940 if (max_completion_time > (timeout * 1000000)) {
839 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); 941 printf("max_completion_time: %ld timeout: %u\n", max_completion_time, timeout);
840 printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1); 942 printf("Timeout must be at least %ld\n", (max_completion_time / 1000000) + 1);
841 } 943 }
842 } 944 }
843 945
844 if (debug) { 946 if (debug) {
845 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); 947 printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl,
846 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval); 948 config.warn.rta, config.warn.pl);
847 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout); 949 printf("target_interval: %ld\n", config.target_interval);
950 printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_data_size + ICMP_MINLEN, timeout);
848 } 951 }
849 952
850 if (packets > 20) { 953 if (config.min_hosts_alive < -1) {
851 errno = 0; 954 errno = 0;
852 crash("packets is > 20 (%d)", packets); 955 crash("minimum alive hosts is negative (%i)", config.min_hosts_alive);
853 } 956 }
854 957
855 if (min_hosts_alive < -1) { 958 // Build an index table of all targets
856 errno = 0; 959 ping_target *host = config.targets;
857 crash("minimum alive hosts is negative (%i)", min_hosts_alive); 960 ping_target **table = malloc(sizeof(ping_target *) * config.number_of_targets);
858 }
859
860 host = list;
861 table = malloc(sizeof(struct rta_host *) * targets);
862 if (!table) { 961 if (!table) {
863 crash("main(): malloc failed for host table"); 962 crash("main(): malloc failed for host table");
864 } 963 }
865 964
866 i = 0; 965 unsigned short target_index = 0;
867 while (host) { 966 while (host) {
868 host->id = i * packets; 967 host->id = target_index * config.number_of_packets;
869 table[i] = host; 968 table[target_index] = host;
870 host = host->next; 969 host = host->next;
871 i++; 970 target_index++;
872 } 971 }
873 972
874 run_checks(); 973 time_t target_interval = config.target_interval;
974
975 check_icmp_state program_state = check_icmp_state_init();
976
977 run_checks(config.icmp_data_size, &target_interval, config.sender_id, config.mode,
978 max_completion_time, prog_start, table, config.number_of_packets, sockset,
979 config.number_of_targets, &program_state);
875 980
876 errno = 0; 981 errno = 0;
877 finish(0);
878 982
879 return (0); 983 mp_check overall = mp_check_init();
880} 984 finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit,
985 config.number_of_targets, &program_state, config.hosts, config.number_of_hosts,
986 &overall);
881 987
882static void run_checks(void) { 988 if (sockset.socket4) {
883 u_int i, t; 989 close(sockset.socket4);
884 u_int final_wait, time_passed; 990 }
991 if (sockset.socket6) {
992 close(sockset.socket6);
993 }
994
995 mp_exit(overall);
996}
885 997
998static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval,
999 const uint16_t sender_id, const check_icmp_execution_mode mode,
1000 const time_t max_completion_time, const struct timeval prog_start,
1001 ping_target **table, const unsigned short packets,
1002 const check_icmp_socket_set sockset, const unsigned short number_of_targets,
1003 check_icmp_state *program_state) {
886 /* this loop might actually violate the pkt_interval or target_interval 1004 /* this loop might actually violate the pkt_interval or target_interval
887 * settings, but only if there aren't any packets on the wire which 1005 * settings, but only if there aren't any packets on the wire which
888 * indicates that the target can handle an increased packet rate */ 1006 * indicates that the target can handle an increased packet rate */
889 for (i = 0; i < packets; i++) { 1007 for (unsigned int packet_index = 0; packet_index < packets; packet_index++) {
890 for (t = 0; t < targets; t++) { 1008 for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) {
891 /* don't send useless packets */ 1009 /* don't send useless packets */
892 if (!targets_alive) { 1010 if (!targets_alive(number_of_targets, program_state->targets_down)) {
893 finish(0); 1011 return;
894 } 1012 }
895 if (table[t]->flags & FLAG_LOST_CAUSE) { 1013 if (table[target_index]->flags & FLAG_LOST_CAUSE) {
896 if (debug) { 1014 if (debug) {
897 printf("%s is a lost cause. not sending any more\n", table[t]->name); 1015
1016 char address[INET6_ADDRSTRLEN];
1017 parse_address(&table[target_index]->address, address, sizeof(address));
1018 printf("%s is a lost cause. not sending any more\n", address);
898 } 1019 }
899 continue; 1020 continue;
900 } 1021 }
901 1022
902 /* we're still in the game, so send next packet */ 1023 /* we're still in the game, so send next packet */
903 (void)send_icmp_ping(icmp_sock, table[t]); 1024 (void)send_icmp_ping(sockset, table[target_index], icmp_pkt_size, sender_id,
904 wait_for_reply(icmp_sock, target_interval); 1025 program_state);
1026
1027 /* wrap up if all targets are declared dead */
1028 if (targets_alive(number_of_targets, program_state->targets_down) ||
1029 get_timevaldiff(prog_start, prog_start) < max_completion_time ||
1030 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1031 wait_for_reply(sockset, *target_interval, icmp_pkt_size, target_interval, sender_id,
1032 table, packets, number_of_targets, program_state);
1033 }
1034 }
1035 if (targets_alive(number_of_targets, program_state->targets_down) ||
1036 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1037 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1038 wait_for_reply(sockset, number_of_targets, icmp_pkt_size, target_interval, sender_id,
1039 table, packets, number_of_targets, program_state);
905 } 1040 }
906 wait_for_reply(icmp_sock, pkt_interval * targets);
907 } 1041 }
908 1042
909 if (icmp_pkts_en_route && targets_alive) { 1043 if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
910 time_passed = get_timevaldiff(NULL, NULL); 1044 program_state->icmp_lost) &&
911 final_wait = max_completion_time - time_passed; 1045 targets_alive(number_of_targets, program_state->targets_down)) {
1046 time_t time_passed = get_timevaldiff_to_now(prog_start);
1047 time_t final_wait = max_completion_time - time_passed;
912 1048
913 if (debug) { 1049 if (debug) {
914 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); 1050 printf("time_passed: %ld final_wait: %ld max_completion_time: %ld\n", time_passed,
1051 final_wait, max_completion_time);
915 } 1052 }
916 if (time_passed > max_completion_time) { 1053 if (time_passed > max_completion_time) {
917 if (debug) { 1054 if (debug) {
918 printf("Time passed. Finishing up\n"); 1055 printf("Time passed. Finishing up\n");
919 } 1056 }
920 finish(0); 1057 return;
921 } 1058 }
922 1059
923 /* catch the packets that might come in within the timeframe, but 1060 /* catch the packets that might come in within the timeframe, but
924 * haven't yet */ 1061 * haven't yet */
925 if (debug) { 1062 if (debug) {
926 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); 1063 printf("Waiting for %ld micro-seconds (%0.3f msecs)\n", final_wait,
1064 (float)final_wait / 1000);
1065 }
1066 if (targets_alive(number_of_targets, program_state->targets_down) ||
1067 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1068 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1069 wait_for_reply(sockset, final_wait, icmp_pkt_size, target_interval, sender_id, table,
1070 packets, number_of_targets, program_state);
927 } 1071 }
928 wait_for_reply(icmp_sock, final_wait);
929 } 1072 }
930} 1073}
931 1074
@@ -939,18 +1082,11 @@ static void run_checks(void) {
939 * both: 1082 * both:
940 * icmp echo reply : the rest 1083 * icmp echo reply : the rest
941 */ 1084 */
942static int wait_for_reply(int sock, u_int t) { 1085static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval,
943 int n, hlen; 1086 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
944 static unsigned char buf[65536]; 1087 ping_target **table, const unsigned short packets,
945 struct sockaddr_storage resp_addr; 1088 const unsigned short number_of_targets, check_icmp_state *program_state) {
946 union ip_hdr *ip;
947 union icmp_packet packet; 1089 union icmp_packet packet;
948 struct rta_host *host;
949 struct icmp_ping_data data;
950 struct timeval wait_start, now;
951 u_int tdiff, i, per_pkt_wait;
952 double jitter_tmp;
953
954 if (!(packet.buf = malloc(icmp_pkt_size))) { 1090 if (!(packet.buf = malloc(icmp_pkt_size))) {
955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1091 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
956 return -1; /* might be reached if we're in debug mode */ 1092 return -1; /* might be reached if we're in debug mode */
@@ -959,177 +1095,174 @@ static int wait_for_reply(int sock, u_int t) {
959 memset(packet.buf, 0, icmp_pkt_size); 1095 memset(packet.buf, 0, icmp_pkt_size);
960 1096
961 /* if we can't listen or don't have anything to listen to, just return */ 1097 /* if we can't listen or don't have anything to listen to, just return */
962 if (!t || !icmp_pkts_en_route) { 1098 if (!time_interval || !icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
1099 program_state->icmp_lost)) {
963 free(packet.buf); 1100 free(packet.buf);
964 return 0; 1101 return 0;
965 } 1102 }
966 1103
967 gettimeofday(&wait_start, &tz); 1104 // Get current time stamp
1105 struct timeval wait_start;
1106 gettimeofday(&wait_start, NULL);
968 1107
969 i = t; 1108 struct sockaddr_storage resp_addr;
970 per_pkt_wait = t / icmp_pkts_en_route; 1109 time_t per_pkt_wait =
971 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) { 1110 time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
972 t = per_pkt_wait; 1111 program_state->icmp_lost);
973 1112 static unsigned char buf[65536];
974 /* wrap up if all targets are declared dead */ 1113 union ip_hdr *ip_header;
975 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) { 1114 struct timeval packet_received_timestamp;
976 finish(0); 1115 while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
977 } 1116 program_state->icmp_lost) &&
1117 get_timevaldiff_to_now(wait_start) < time_interval) {
1118 time_t loop_time_interval = per_pkt_wait;
978 1119
979 /* reap responses until we hit a timeout */ 1120 /* reap responses until we hit a timeout */
980 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); 1121 recvfrom_wto_wrapper recv_foo =
981 if (!n) { 1122 recvfrom_wto(sockset, buf, sizeof(buf), (struct sockaddr *)&resp_addr,
1123 &loop_time_interval, &packet_received_timestamp);
1124 if (!recv_foo.received) {
982 if (debug > 1) { 1125 if (debug > 1) {
983 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); 1126 printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait);
984 } 1127 }
985 continue; /* timeout for this one, so keep trying */ 1128 continue; /* timeout for this one, so keep trying */
986 } 1129 }
987 if (n < 0) { 1130
1131 if (recv_foo.received < 0) {
988 if (debug) { 1132 if (debug) {
989 printf("recvfrom_wto() returned errors\n"); 1133 printf("recvfrom_wto() returned errors\n");
990 } 1134 }
991 free(packet.buf); 1135 free(packet.buf);
992 return n; 1136 return (int)recv_foo.received;
993 } 1137 }
994 1138
995 // FIXME: with ipv6 we don't have an ip header here 1139 if (recv_foo.recv_proto != AF_INET6) {
996 if (address_family != AF_INET6) { 1140 ip_header = (union ip_hdr *)buf;
997 ip = (union ip_hdr *)buf;
998 1141
999 if (debug > 1) { 1142 if (debug > 1) {
1000 char address[INET6_ADDRSTRLEN]; 1143 char address[INET6_ADDRSTRLEN];
1001 parse_address(&resp_addr, address, sizeof(address)); 1144 parse_address(&resp_addr, address, sizeof(address));
1002 printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address); 1145 printf("received %u bytes from %s\n",
1146 address_family == AF_INET6 ? ntohs(ip_header->ip6.ip6_plen)
1147 : ntohs(ip_header->ip.ip_len),
1148 address);
1003 } 1149 }
1004 } 1150 }
1005 1151
1006 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 1152 int hlen = (recv_foo.recv_proto == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2;
1007 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 1153
1008 /* alpha headers are decidedly broken. Using an ansi compiler, 1154 if (recv_foo.received < (hlen + ICMP_MINLEN)) {
1009 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
1010 * off the bottom 4 bits */
1011 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
1012 /* #else */
1013 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
1014 /* #endif */
1015
1016 if (n < (hlen + ICMP_MINLEN)) {
1017 char address[INET6_ADDRSTRLEN]; 1155 char address[INET6_ADDRSTRLEN];
1018 parse_address(&resp_addr, address, sizeof(address)); 1156 parse_address(&resp_addr, address, sizeof(address));
1019 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address); 1157 crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n",
1158 recv_foo.received, hlen + icmp_pkt_size, address);
1020 } 1159 }
1021 /* else if(debug) { */
1022 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
1023 /* hlen, ntohs(ip->ip_len) - hlen, */
1024 /* sizeof(struct ip), icmp_pkt_size); */
1025 /* } */
1026
1027 /* check the response */ 1160 /* check the response */
1028
1029 memcpy(packet.buf, buf + hlen, icmp_pkt_size); 1161 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
1030 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
1031 : sizeof(struct icmp));*/
1032 1162
1033 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || 1163 if ((recv_foo.recv_proto == AF_INET &&
1034 ntohs(packet.icp->icmp_seq) >= targets * packets)) || 1164 (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY ||
1035 (address_family == PF_INET6 && (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || 1165 ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) ||
1036 ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { 1166 (recv_foo.recv_proto == AF_INET6 &&
1167 (ntohs(packet.icp6->icmp6_id) != sender_id ||
1168 packet.icp6->icmp6_type != ICMP6_ECHO_REPLY ||
1169 ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) {
1037 if (debug > 2) { 1170 if (debug > 2) {
1038 printf("not a proper ICMP_ECHOREPLY\n"); 1171 printf("not a proper ICMP_ECHOREPLY\n");
1039 } 1172 }
1040 handle_random_icmp(buf + hlen, &resp_addr); 1173
1174 handle_random_icmp(buf + hlen, &resp_addr, target_interval, sender_id, table, packets,
1175 number_of_targets, program_state);
1176
1041 continue; 1177 continue;
1042 } 1178 }
1043 1179
1044 /* this is indeed a valid response */ 1180 /* this is indeed a valid response */
1045 if (address_family == PF_INET) { 1181 ping_target *target;
1182 struct icmp_ping_data data;
1183 if (address_family == AF_INET) {
1046 memcpy(&data, packet.icp->icmp_data, sizeof(data)); 1184 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1047 if (debug > 2) { 1185 if (debug > 2) {
1048 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 1186 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1049 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); 1187 ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq),
1188 packet.icp->icmp_cksum);
1050 } 1189 }
1051 host = table[ntohs(packet.icp->icmp_seq) / packets]; 1190 target = table[ntohs(packet.icp->icmp_seq) / packets];
1052 } else { 1191 } else {
1053 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); 1192 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1054 if (debug > 2) { 1193 if (debug > 2) {
1055 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), 1194 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1056 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); 1195 ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq),
1196 packet.icp6->icmp6_cksum);
1057 } 1197 }
1058 host = table[ntohs(packet.icp6->icmp6_seq) / packets]; 1198 target = table[ntohs(packet.icp6->icmp6_seq) / packets];
1059 } 1199 }
1060 1200
1061 tdiff = get_timevaldiff(&data.stime, &now); 1201 time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp);
1062 1202
1063 if (host->last_tdiff > 0) { 1203 if (target->last_tdiff > 0) {
1064 /* Calculate jitter */ 1204 /* Calculate jitter */
1065 if (host->last_tdiff > tdiff) { 1205 double jitter_tmp;
1066 jitter_tmp = host->last_tdiff - tdiff; 1206 if (target->last_tdiff > tdiff) {
1207 jitter_tmp = (double)(target->last_tdiff - tdiff);
1067 } else { 1208 } else {
1068 jitter_tmp = tdiff - host->last_tdiff; 1209 jitter_tmp = (double)(tdiff - target->last_tdiff);
1069 } 1210 }
1070 1211
1071 if (host->jitter == 0) { 1212 if (target->jitter == 0) {
1072 host->jitter = jitter_tmp; 1213 target->jitter = jitter_tmp;
1073 host->jitter_max = jitter_tmp; 1214 target->jitter_max = jitter_tmp;
1074 host->jitter_min = jitter_tmp; 1215 target->jitter_min = jitter_tmp;
1075 } else { 1216 } else {
1076 host->jitter += jitter_tmp; 1217 target->jitter += jitter_tmp;
1077 1218
1078 if (jitter_tmp < host->jitter_min) { 1219 if (jitter_tmp < target->jitter_min) {
1079 host->jitter_min = jitter_tmp; 1220 target->jitter_min = jitter_tmp;
1080 } 1221 }
1081 1222
1082 if (jitter_tmp > host->jitter_max) { 1223 if (jitter_tmp > target->jitter_max) {
1083 host->jitter_max = jitter_tmp; 1224 target->jitter_max = jitter_tmp;
1084 } 1225 }
1085 } 1226 }
1086 1227
1087 /* Check if packets in order */ 1228 /* Check if packets in order */
1088 if (host->last_icmp_seq >= packet.icp->icmp_seq) { 1229 if (target->last_icmp_seq >= packet.icp->icmp_seq) {
1089 host->order_status = STATE_CRITICAL; 1230 target->found_out_of_order_packets = true;
1090 } 1231 }
1091 } 1232 }
1092 host->last_tdiff = tdiff; 1233 target->last_tdiff = tdiff;
1093 1234
1094 host->last_icmp_seq = packet.icp->icmp_seq; 1235 target->last_icmp_seq = packet.icp->icmp_seq;
1095 1236
1096 host->time_waited += tdiff; 1237 target->time_waited += tdiff;
1097 host->icmp_recv++; 1238 target->icmp_recv++;
1098 icmp_recv++; 1239 program_state->icmp_recv++;
1099 1240
1100 if (tdiff > (unsigned int)host->rtmax) { 1241 if (tdiff > (unsigned int)target->rtmax) {
1101 host->rtmax = tdiff; 1242 target->rtmax = (double)tdiff;
1102 } 1243 }
1103 1244
1104 if ((host->rtmin == INFINITY) || (tdiff < (unsigned int)host->rtmin)) { 1245 if ((target->rtmin == INFINITY) || (tdiff < (unsigned int)target->rtmin)) {
1105 host->rtmin = tdiff; 1246 target->rtmin = (double)tdiff;
1106 } 1247 }
1107 1248
1108 if (debug) { 1249 if (debug) {
1109 char address[INET6_ADDRSTRLEN]; 1250 char address[INET6_ADDRSTRLEN];
1110 parse_address(&resp_addr, address, sizeof(address)); 1251 parse_address(&resp_addr, address, sizeof(address));
1111 1252
1112 switch (address_family) { 1253 switch (recv_foo.recv_proto) {
1113 case AF_INET: { 1254 case AF_INET: {
1114 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, 1255 printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
1115 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1256 (float)tdiff / 1000, address, ip_header->ip.ip_ttl,
1257 (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1116 break; 1258 break;
1117 }; 1259 };
1118 case AF_INET6: { 1260 case AF_INET6: {
1119 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, 1261 printf("%0.3f ms rtt from %s, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000,
1120 (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1262 address, (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1121 }; 1263 };
1122 } 1264 }
1123 } 1265 }
1124
1125 /* if we're in hostcheck mode, exit with limited printouts */
1126 if (mode == MODE_HOSTCHECK) {
1127 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1128 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1129 host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000,
1130 (float)crit.rta / 1000);
1131 exit(STATE_OK);
1132 }
1133 } 1266 }
1134 1267
1135 free(packet.buf); 1268 free(packet.buf);
@@ -1137,38 +1270,28 @@ static int wait_for_reply(int sock, u_int t) {
1137} 1270}
1138 1271
1139/* the ping functions */ 1272/* the ping functions */
1140static int send_icmp_ping(int sock, struct rta_host *host) { 1273static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host,
1141 long int len; 1274 const unsigned short icmp_pkt_size, const uint16_t sender_id,
1142 size_t addrlen; 1275 check_icmp_state *program_state) {
1143 struct icmp_ping_data data; 1276 void *buf = calloc(1, icmp_pkt_size);
1144 struct msghdr hdr;
1145 struct iovec iov;
1146 struct timeval tv;
1147 void *buf = NULL;
1148
1149 if (sock == -1) {
1150 errno = 0;
1151 crash("Attempt to send on bogus socket");
1152 return -1;
1153 }
1154
1155 if (!buf) { 1277 if (!buf) {
1156 if (!(buf = malloc(icmp_pkt_size))) { 1278 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
1157 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1279 return -1; /* might be reached if we're in debug mode */
1158 return -1; /* might be reached if we're in debug mode */
1159 }
1160 } 1280 }
1161 memset(buf, 0, icmp_pkt_size);
1162 1281
1163 if ((gettimeofday(&tv, &tz)) == -1) { 1282 struct timeval current_time;
1283 if ((gettimeofday(&current_time, NULL)) == -1) {
1164 free(buf); 1284 free(buf);
1165 return -1; 1285 return -1;
1166 } 1286 }
1167 1287
1288 struct icmp_ping_data data;
1168 data.ping_id = 10; /* host->icmp.icmp_sent; */ 1289 data.ping_id = 10; /* host->icmp.icmp_sent; */
1169 memcpy(&data.stime, &tv, sizeof(tv)); 1290 memcpy(&data.stime, &current_time, sizeof(current_time));
1170 1291
1171 if (address_family == AF_INET) { 1292 socklen_t addrlen = 0;
1293
1294 if (host->address.ss_family == AF_INET) {
1172 struct icmp *icp = (struct icmp *)buf; 1295 struct icmp *icp = (struct icmp *)buf;
1173 addrlen = sizeof(struct sockaddr_in); 1296 addrlen = sizeof(struct sockaddr_in);
1174 1297
@@ -1177,15 +1300,19 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1177 icp->icmp_type = ICMP_ECHO; 1300 icp->icmp_type = ICMP_ECHO;
1178 icp->icmp_code = 0; 1301 icp->icmp_code = 0;
1179 icp->icmp_cksum = 0; 1302 icp->icmp_cksum = 0;
1180 icp->icmp_id = htons(pid); 1303 icp->icmp_id = htons((uint16_t)sender_id);
1181 icp->icmp_seq = htons(host->id++); 1304 icp->icmp_seq = htons(host->id++);
1182 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); 1305 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1183 1306
1184 if (debug > 2) { 1307 if (debug > 2) {
1185 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1308 char address[INET6_ADDRSTRLEN];
1186 ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); 1309 parse_address((&host->address), address, sizeof(address));
1310
1311 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1312 sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum,
1313 address);
1187 } 1314 }
1188 } else { 1315 } else if (host->address.ss_family == AF_INET6) {
1189 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; 1316 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1190 addrlen = sizeof(struct sockaddr_in6); 1317 addrlen = sizeof(struct sockaddr_in6);
1191 1318
@@ -1194,659 +1321,431 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1194 icp6->icmp6_type = ICMP6_ECHO_REQUEST; 1321 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1195 icp6->icmp6_code = 0; 1322 icp6->icmp6_code = 0;
1196 icp6->icmp6_cksum = 0; 1323 icp6->icmp6_cksum = 0;
1197 icp6->icmp6_id = htons(pid); 1324 icp6->icmp6_id = htons((uint16_t)sender_id);
1198 icp6->icmp6_seq = htons(host->id++); 1325 icp6->icmp6_seq = htons(host->id++);
1199 // let checksum be calculated automatically 1326 // let checksum be calculated automatically
1200 1327
1201 if (debug > 2) { 1328 if (debug > 2) {
1202 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1329 char address[INET6_ADDRSTRLEN];
1203 ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); 1330 parse_address((&host->address), address, sizeof(address));
1331
1332 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to target %s\n",
1333 sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum,
1334 address);
1204 } 1335 }
1336 } else {
1337 // unknown address family
1338 crash("unknown address family in %s", __func__);
1205 } 1339 }
1206 1340
1341 struct iovec iov;
1207 memset(&iov, 0, sizeof(iov)); 1342 memset(&iov, 0, sizeof(iov));
1208 iov.iov_base = buf; 1343 iov.iov_base = buf;
1209 iov.iov_len = icmp_pkt_size; 1344 iov.iov_len = icmp_pkt_size;
1210 1345
1346 struct msghdr hdr;
1211 memset(&hdr, 0, sizeof(hdr)); 1347 memset(&hdr, 0, sizeof(hdr));
1212 hdr.msg_name = (struct sockaddr *)&host->saddr_in; 1348 hdr.msg_name = (struct sockaddr *)&host->address;
1213 hdr.msg_namelen = addrlen; 1349 hdr.msg_namelen = addrlen;
1214 hdr.msg_iov = &iov; 1350 hdr.msg_iov = &iov;
1215 hdr.msg_iovlen = 1; 1351 hdr.msg_iovlen = 1;
1216 1352
1217 errno = 0; 1353 errno = 0;
1218 1354
1219/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1355 long int len;
1356 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1357 if (host->address.ss_family == AF_INET) {
1358#ifdef MSG_CONFIRM
1359 len = sendmsg(sockset.socket4, &hdr, MSG_CONFIRM);
1360#else
1361 len = sendmsg(sockset.socket4, &hdr, 0);
1362#endif
1363 } else if (host->address.ss_family == AF_INET6) {
1220#ifdef MSG_CONFIRM 1364#ifdef MSG_CONFIRM
1221 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1365 len = sendmsg(sockset.socket6, &hdr, MSG_CONFIRM);
1222#else 1366#else
1223 len = sendmsg(sock, &hdr, 0); 1367 len = sendmsg(sockset.socket6, &hdr, 0);
1224#endif 1368#endif
1369 } else {
1370 assert(false);
1371 }
1225 1372
1226 free(buf); 1373 free(buf);
1227 1374
1228 if (len < 0 || (unsigned int)len != icmp_pkt_size) { 1375 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1229 if (debug) { 1376 if (debug) {
1230 char address[INET6_ADDRSTRLEN]; 1377 char address[INET6_ADDRSTRLEN];
1231 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); 1378 parse_address((&host->address), address, sizeof(address));
1232 printf("Failed to send ping to %s: %s\n", address, strerror(errno)); 1379 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1233 } 1380 }
1234 errno = 0; 1381 errno = 0;
1235 return -1; 1382 return -1;
1236 } 1383 }
1237 1384
1238 icmp_sent++; 1385 program_state->icmp_sent++;
1239 host->icmp_sent++; 1386 host->icmp_sent++;
1240 1387
1241 return 0; 1388 return 0;
1242} 1389}
1243 1390
1244static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { 1391static recvfrom_wto_wrapper recvfrom_wto(const check_icmp_socket_set sockset, void *buf,
1245 u_int slen; 1392 const unsigned int len, struct sockaddr *saddr,
1246 int n, ret; 1393 time_t *timeout, struct timeval *received_timestamp) {
1247 struct timeval to, then, now;
1248 fd_set rd, wr;
1249#ifdef HAVE_MSGHDR_MSG_CONTROL 1394#ifdef HAVE_MSGHDR_MSG_CONTROL
1250 char ans_data[4096]; 1395 char ans_data[4096];
1251#endif // HAVE_MSGHDR_MSG_CONTROL 1396#endif // HAVE_MSGHDR_MSG_CONTROL
1252 struct msghdr hdr;
1253 struct iovec iov;
1254#ifdef SO_TIMESTAMP 1397#ifdef SO_TIMESTAMP
1255 struct cmsghdr *chdr; 1398 struct cmsghdr *chdr;
1256#endif 1399#endif
1257 1400
1258 if (!*timo) { 1401 recvfrom_wto_wrapper result = {
1402 .received = 0,
1403 .recv_proto = AF_UNSPEC,
1404 };
1405
1406 if (!*timeout) {
1259 if (debug) { 1407 if (debug) {
1260 printf("*timo is not\n"); 1408 printf("*timeout is not\n");
1261 } 1409 }
1262 return 0; 1410 return result;
1411 }
1412
1413 struct timeval real_timeout;
1414 real_timeout.tv_sec = *timeout / 1000000;
1415 real_timeout.tv_usec = (*timeout - (real_timeout.tv_sec * 1000000));
1416
1417 // Dummy fds for select
1418 fd_set dummy_write_fds;
1419 FD_ZERO(&dummy_write_fds);
1420
1421 // Read fds for select with the socket
1422 fd_set read_fds;
1423 FD_ZERO(&read_fds);
1424
1425 if (sockset.socket4 != -1) {
1426 FD_SET(sockset.socket4, &read_fds);
1427 }
1428 if (sockset.socket6 != -1) {
1429 FD_SET(sockset.socket6, &read_fds);
1263 } 1430 }
1264 1431
1265 to.tv_sec = *timo / 1000000; 1432 int nfds = (sockset.socket4 > sockset.socket6 ? sockset.socket4 : sockset.socket6) + 1;
1266 to.tv_usec = (*timo - (to.tv_sec * 1000000)); 1433
1434 struct timeval then;
1435 gettimeofday(&then, NULL);
1267 1436
1268 FD_ZERO(&rd);
1269 FD_ZERO(&wr);
1270 FD_SET(sock, &rd);
1271 errno = 0; 1437 errno = 0;
1272 gettimeofday(&then, &tz); 1438 int select_return = select(nfds, &read_fds, &dummy_write_fds, NULL, &real_timeout);
1273 n = select(sock + 1, &rd, &wr, NULL, &to); 1439 if (select_return < 0) {
1274 if (n < 0) {
1275 crash("select() in recvfrom_wto"); 1440 crash("select() in recvfrom_wto");
1276 } 1441 }
1277 gettimeofday(&now, &tz);
1278 *timo = get_timevaldiff(&then, &now);
1279 1442
1280 if (!n) { 1443 struct timeval now;
1281 return 0; /* timeout */ 1444 gettimeofday(&now, NULL);
1445 *timeout = get_timevaldiff(then, now);
1446
1447 if (!select_return) {
1448 return result; /* timeout */
1282 } 1449 }
1283 1450
1284 slen = sizeof(struct sockaddr_storage); 1451 unsigned int slen = sizeof(struct sockaddr_storage);
1285 1452
1286 memset(&iov, 0, sizeof(iov)); 1453 struct iovec iov = {
1287 iov.iov_base = buf; 1454 .iov_base = buf,
1288 iov.iov_len = len; 1455 .iov_len = len,
1456 };
1289 1457
1290 memset(&hdr, 0, sizeof(hdr)); 1458 struct msghdr hdr = {
1291 hdr.msg_name = saddr; 1459 .msg_name = saddr,
1292 hdr.msg_namelen = slen; 1460 .msg_namelen = slen,
1293 hdr.msg_iov = &iov; 1461 .msg_iov = &iov,
1294 hdr.msg_iovlen = 1; 1462 .msg_iovlen = 1,
1295#ifdef HAVE_MSGHDR_MSG_CONTROL 1463#ifdef HAVE_MSGHDR_MSG_CONTROL
1296 hdr.msg_control = ans_data; 1464 .msg_control = ans_data,
1297 hdr.msg_controllen = sizeof(ans_data); 1465 .msg_controllen = sizeof(ans_data),
1298#endif 1466#endif
1467 };
1468
1469 ssize_t ret;
1470 if (FD_ISSET(sockset.socket4, &read_fds)) {
1471 ret = recvmsg(sockset.socket4, &hdr, 0);
1472 result.recv_proto = AF_INET;
1473 } else if (FD_ISSET(sockset.socket6, &read_fds)) {
1474 ret = recvmsg(sockset.socket6, &hdr, 0);
1475 result.recv_proto = AF_INET6;
1476 } else {
1477 assert(false);
1478 }
1479
1480 result.received = ret;
1299 1481
1300 ret = recvmsg(sock, &hdr, 0);
1301#ifdef SO_TIMESTAMP 1482#ifdef SO_TIMESTAMP
1302 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { 1483 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1303 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { 1484 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP &&
1304 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); 1485 chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1486 memcpy(received_timestamp, CMSG_DATA(chdr), sizeof(*received_timestamp));
1305 break; 1487 break;
1306 } 1488 }
1307 } 1489 }
1308 1490
1309 if (!chdr) 1491 if (!chdr) {
1492 gettimeofday(received_timestamp, NULL);
1493 }
1494#else
1495 gettimeofday(tv, NULL);
1310#endif // SO_TIMESTAMP 1496#endif // SO_TIMESTAMP
1311 gettimeofday(tv, &tz);
1312 return (ret);
1313}
1314 1497
1315static void finish(int sig) { 1498 return (result);
1316 u_int i = 0; 1499}
1317 unsigned char pl;
1318 double rta;
1319 struct rta_host *host;
1320 const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1321 int hosts_ok = 0;
1322 int hosts_warn = 0;
1323 int this_status;
1324 double R;
1325 1500
1501static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive,
1502 check_icmp_threshold warn, check_icmp_threshold crit,
1503 const unsigned short number_of_targets, check_icmp_state *program_state,
1504 check_icmp_target_container host_list[], unsigned short number_of_hosts,
1505 mp_check overall[static 1]) {
1506 // Deactivate alarm
1326 alarm(0); 1507 alarm(0);
1508
1327 if (debug > 1) { 1509 if (debug > 1) {
1328 printf("finish(%d) called\n", sig); 1510 printf("finish(%d) called\n", sig);
1329 } 1511 }
1330 1512
1331 if (icmp_sock != -1) {
1332 close(icmp_sock);
1333 }
1334 if (udp_sock != -1) {
1335 close(udp_sock);
1336 }
1337 if (tcp_sock != -1) {
1338 close(tcp_sock);
1339 }
1340
1341 if (debug) { 1513 if (debug) {
1342 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost); 1514 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", program_state->icmp_sent,
1343 printf("targets: %u targets_alive: %u\n", targets, targets_alive); 1515 program_state->icmp_recv, program_state->icmp_lost);
1516 printf("targets: %u targets_alive: %u\n", number_of_targets,
1517 targets_alive(number_of_targets, program_state->targets_down));
1344 } 1518 }
1345 1519
1346 /* iterate thrice to calculate values, give output, and print perfparse */ 1520 // loop over targets to evaluate each one
1347 status = STATE_OK; 1521 int targets_ok = 0;
1348 host = list; 1522 int targets_warn = 0;
1349 1523 for (unsigned short i = 0; i < number_of_hosts; i++) {
1350 while (host) { 1524 evaluate_host_wrapper host_check = evaluate_host(host_list[i], modes, warn, crit);
1351 this_status = STATE_OK;
1352
1353 if (!host->icmp_recv) {
1354 /* rta 0 is ofcourse not entirely correct, but will still show up
1355 * conspicuously as missing entries in perfparse and cacti */
1356 pl = 100;
1357 rta = 0;
1358 status = STATE_CRITICAL;
1359 /* up the down counter if not already counted */
1360 if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive) {
1361 targets_down++;
1362 }
1363 } else {
1364 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1365 rta = (double)host->time_waited / host->icmp_recv;
1366 }
1367
1368 if (host->icmp_recv > 1) {
1369 /*
1370 * This algorithm is probably pretty much blindly copied from
1371 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1372 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1373 * According to some quick research MOS originates from the Audio/Video transport network area.
1374 * Whether it can and should be computed from ICMP data, I can not say.
1375 *
1376 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1377 *
1378 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1379 *
1380 * More links:
1381 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1382 */
1383 host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000);
1384
1385 /*
1386 * Take the average round trip latency (in milliseconds), add
1387 * round trip jitter, but double the impact to latency
1388 * then add 10 for protocol latencies (in milliseconds).
1389 */
1390 host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10;
1391
1392 if (host->EffectiveLatency < 160) {
1393 R = 93.2 - (host->EffectiveLatency / 40);
1394 } else {
1395 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1396 }
1397
1398 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1399 // loss of 5% will be entered as 5).
1400 R = R - (pl * 2.5);
1401
1402 if (R < 0) {
1403 R = 0;
1404 }
1405
1406 host->score = R;
1407 host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
1408 } else {
1409 host->jitter = 0;
1410 host->jitter_min = 0;
1411 host->jitter_max = 0;
1412 host->mos = 0;
1413 }
1414
1415 host->pl = pl;
1416 host->rta = rta;
1417
1418 /* if no new mode selected, use old schema */
1419 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1420 rta_mode = true;
1421 pl_mode = true;
1422 }
1423
1424 /* Check which mode is on and do the warn / Crit stuff */
1425 if (rta_mode) {
1426 if (rta >= crit.rta) {
1427 this_status = STATE_CRITICAL;
1428 status = STATE_CRITICAL;
1429 host->rta_status = STATE_CRITICAL;
1430 } else if (status != STATE_CRITICAL && (rta >= warn.rta)) {
1431 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1432 status = STATE_WARNING;
1433 host->rta_status = STATE_WARNING;
1434 }
1435 }
1436
1437 if (pl_mode) {
1438 if (pl >= crit.pl) {
1439 this_status = STATE_CRITICAL;
1440 status = STATE_CRITICAL;
1441 host->pl_status = STATE_CRITICAL;
1442 } else if (status != STATE_CRITICAL && (pl >= warn.pl)) {
1443 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1444 status = STATE_WARNING;
1445 host->pl_status = STATE_WARNING;
1446 }
1447 }
1448
1449 if (jitter_mode) {
1450 if (host->jitter >= crit.jitter) {
1451 this_status = STATE_CRITICAL;
1452 status = STATE_CRITICAL;
1453 host->jitter_status = STATE_CRITICAL;
1454 } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1455 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1456 status = STATE_WARNING;
1457 host->jitter_status = STATE_WARNING;
1458 }
1459 }
1460
1461 if (mos_mode) {
1462 if (host->mos <= crit.mos) {
1463 this_status = STATE_CRITICAL;
1464 status = STATE_CRITICAL;
1465 host->mos_status = STATE_CRITICAL;
1466 } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) {
1467 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1468 status = STATE_WARNING;
1469 host->mos_status = STATE_WARNING;
1470 }
1471 }
1472
1473 if (score_mode) {
1474 if (host->score <= crit.score) {
1475 this_status = STATE_CRITICAL;
1476 status = STATE_CRITICAL;
1477 host->score_status = STATE_CRITICAL;
1478 } else if (status != STATE_CRITICAL && (host->score <= warn.score)) {
1479 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1480 status = STATE_WARNING;
1481 host->score_status = STATE_WARNING;
1482 }
1483 }
1484 1525
1485 if (this_status == STATE_WARNING) { 1526 targets_ok += host_check.targets_ok;
1486 hosts_warn++; 1527 targets_warn += host_check.targets_warn;
1487 } else if (this_status == STATE_OK) {
1488 hosts_ok++;
1489 }
1490 1528
1491 host = host->next; 1529 mp_add_subcheck_to_check(overall, host_check.sc_host);
1492 } 1530 }
1493 1531
1494 /* this is inevitable */
1495 if (!targets_alive) {
1496 status = STATE_CRITICAL;
1497 }
1498 if (min_hosts_alive > -1) { 1532 if (min_hosts_alive > -1) {
1499 if (hosts_ok >= min_hosts_alive) { 1533 mp_subcheck sc_min_targets_alive = mp_subcheck_init();
1500 status = STATE_OK; 1534 sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK);
1501 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { 1535
1502 status = STATE_WARNING; 1536 if (targets_ok >= min_hosts_alive) {
1503 } 1537 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK);
1504 } 1538 xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", targets_ok,
1505 printf("%s - ", status_string[status]); 1539 min_hosts_alive);
1506 1540
1507 host = list; 1541 // Overwrite main state here
1508 while (host) { 1542 overall->evaluation_function = &mp_eval_ok;
1509 if (debug) { 1543 } else if ((targets_ok + targets_warn) >= min_hosts_alive) {
1510 puts(""); 1544 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING);
1511 } 1545 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1512 1546 targets_ok + targets_warn, min_hosts_alive);
1513 if (i) { 1547 overall->evaluation_function = &mp_eval_warning;
1514 if (i < targets) { 1548 } else {
1515 printf(" :: "); 1549 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL);
1516 } else { 1550 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1517 printf("\n"); 1551 targets_ok + targets_warn, min_hosts_alive);
1518 } 1552 overall->evaluation_function = &mp_eval_critical;
1519 }
1520
1521 i++;
1522
1523 if (!host->icmp_recv) {
1524 status = STATE_CRITICAL;
1525 host->rtmin = 0;
1526 host->jitter_min = 0;
1527
1528 if (host->flags & FLAG_LOST_CAUSE) {
1529 char address[INET6_ADDRSTRLEN];
1530 parse_address(&host->error_addr, address, sizeof(address));
1531 printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100);
1532 } else { /* not marked as lost cause, so we have no flags for it */
1533 printf("%s: rta nan, lost 100%%", host->name);
1534 }
1535 } else { /* !icmp_recv */
1536 printf("%s", host->name);
1537 /* rta text output */
1538 if (rta_mode) {
1539 if (status == STATE_OK) {
1540 printf(" rta %0.3fms", host->rta / 1000);
1541 } else if (status == STATE_WARNING && host->rta_status == status) {
1542 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000);
1543 } else if (status == STATE_CRITICAL && host->rta_status == status) {
1544 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000);
1545 }
1546 }
1547
1548 /* pl text output */
1549 if (pl_mode) {
1550 if (status == STATE_OK) {
1551 printf(" lost %u%%", host->pl);
1552 } else if (status == STATE_WARNING && host->pl_status == status) {
1553 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1554 } else if (status == STATE_CRITICAL && host->pl_status == status) {
1555 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1556 }
1557 }
1558
1559 /* jitter text output */
1560 if (jitter_mode) {
1561 if (status == STATE_OK) {
1562 printf(" jitter %0.3fms", (float)host->jitter);
1563 } else if (status == STATE_WARNING && host->jitter_status == status) {
1564 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1565 } else if (status == STATE_CRITICAL && host->jitter_status == status) {
1566 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1567 }
1568 }
1569
1570 /* mos text output */
1571 if (mos_mode) {
1572 if (status == STATE_OK) {
1573 printf(" MOS %0.1f", (float)host->mos);
1574 } else if (status == STATE_WARNING && host->mos_status == status) {
1575 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1576 } else if (status == STATE_CRITICAL && host->mos_status == status) {
1577 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1578 }
1579 }
1580
1581 /* score text output */
1582 if (score_mode) {
1583 if (status == STATE_OK) {
1584 printf(" Score %u", (int)host->score);
1585 } else if (status == STATE_WARNING && host->score_status == status) {
1586 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1587 } else if (status == STATE_CRITICAL && host->score_status == status) {
1588 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1589 }
1590 }
1591
1592 /* order statis text output */
1593 if (order_mode) {
1594 if (status == STATE_OK) {
1595 printf(" Packets in order");
1596 } else if (status == STATE_CRITICAL && host->order_status == status) {
1597 printf(" Packets out of order");
1598 }
1599 }
1600 }
1601 host = host->next;
1602 }
1603
1604 /* iterate once more for pretty perfparse output */
1605 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1606 printf("|");
1607 }
1608 i = 0;
1609 host = list;
1610 while (host) {
1611 if (debug) {
1612 puts("");
1613 }
1614
1615 if (rta_mode) {
1616 if (host->pl < 100) {
1617 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "",
1618 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, (targets > 1) ? host->name : "",
1619 (float)host->rtmax / 1000, (targets > 1) ? host->name : "",
1620 (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1621 } else {
1622 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "",
1623 (targets > 1) ? host->name : "");
1624 }
1625 }
1626
1627 if (pl_mode) {
1628 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1629 }
1630
1631 if (jitter_mode) {
1632 if (host->pl < 100) {
1633 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1634 (targets > 1) ? host->name : "", (float)host->jitter, (float)warn.jitter, (float)crit.jitter,
1635 (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1636 (float)host->jitter_min / 1000);
1637 } else {
1638 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "",
1639 (targets > 1) ? host->name : "", (targets > 1) ? host->name : "");
1640 }
1641 }
1642
1643 if (mos_mode) {
1644 if (host->pl < 100) {
1645 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos);
1646 } else {
1647 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1648 }
1649 }
1650
1651 if (score_mode) {
1652 if (host->pl < 100) {
1653 printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score);
1654 } else {
1655 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1656 }
1657 } 1553 }
1658 1554
1659 host = host->next; 1555 mp_add_subcheck_to_check(overall, sc_min_targets_alive);
1660 }
1661
1662 if (min_hosts_alive > -1) {
1663 if (hosts_ok >= min_hosts_alive) {
1664 status = STATE_OK;
1665 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) {
1666 status = STATE_WARNING;
1667 }
1668 } 1556 }
1669 1557
1670 /* finish with an empty line */ 1558 /* finish with an empty line */
1671 puts("");
1672 if (debug) { 1559 if (debug) {
1673 printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok, 1560 printf(
1674 hosts_warn, min_hosts_alive); 1561 "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1562 number_of_targets, targets_alive(number_of_targets, program_state->targets_down),
1563 targets_ok, targets_warn, min_hosts_alive);
1675 } 1564 }
1676
1677 exit(status);
1678} 1565}
1679 1566
1680static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { 1567static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) {
1681 u_int ret;
1682 struct timeval now;
1683
1684 if (!later) {
1685 gettimeofday(&now, &tz);
1686 later = &now;
1687 }
1688 if (!early) {
1689 early = &prog_start;
1690 }
1691
1692 /* if early > later we return 0 so as to indicate a timeout */ 1568 /* if early > later we return 0 so as to indicate a timeout */
1693 if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { 1569 if (earlier.tv_sec > later.tv_sec ||
1570 (earlier.tv_sec == later.tv_sec && earlier.tv_usec > later.tv_usec)) {
1694 return 0; 1571 return 0;
1695 } 1572 }
1696 ret = (later->tv_sec - early->tv_sec) * 1000000; 1573
1697 ret += later->tv_usec - early->tv_usec; 1574 time_t ret = (later.tv_sec - earlier.tv_sec) * 1000000;
1575 ret += later.tv_usec - earlier.tv_usec;
1698 1576
1699 return ret; 1577 return ret;
1700} 1578}
1701 1579
1702static int add_target_ip(char *arg, struct sockaddr_storage *in) { 1580static time_t get_timevaldiff_to_now(struct timeval earlier) {
1703 struct rta_host *host; 1581 struct timeval now;
1704 struct sockaddr_in *sin, *host_sin; 1582 gettimeofday(&now, NULL);
1705 struct sockaddr_in6 *sin6, *host_sin6; 1583
1584 return get_timevaldiff(earlier, now);
1585}
1586
1587static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) {
1588 assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6));
1706 1589
1707 if (address_family == AF_INET) { 1590 if (debug) {
1708 sin = (struct sockaddr_in *)in; 1591 char straddr[INET6_ADDRSTRLEN];
1592 parse_address((&address), straddr, sizeof(straddr));
1593 printf("add_target_ip called with: %s\n", straddr);
1594 }
1595 struct sockaddr_in *sin;
1596 struct sockaddr_in6 *sin6;
1597 if (address.ss_family == AF_INET) {
1598 sin = (struct sockaddr_in *)&address;
1599 } else if (address.ss_family == AF_INET6) {
1600 sin6 = (struct sockaddr_in6 *)&address;
1709 } else { 1601 } else {
1710 sin6 = (struct sockaddr_in6 *)in; 1602 assert(false);
1711 } 1603 }
1712 1604
1605 add_target_ip_wrapper result = {
1606 .error_code = OK,
1607 .target = NULL,
1608 };
1609
1713 /* disregard obviously stupid addresses 1610 /* disregard obviously stupid addresses
1714 * (I didn't find an ipv6 equivalent to INADDR_NONE) */ 1611 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1715 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || 1612 if (((address.ss_family == AF_INET &&
1716 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { 1613 (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1717 return -1; 1614 (address.ss_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1615 result.error_code = ERROR;
1616 return result;
1718 } 1617 }
1719 1618
1720 /* no point in adding two identical IP's, so don't. ;) */ 1619 // get string representation of address
1721 host = list; 1620 char straddr[INET6_ADDRSTRLEN];
1722 while (host) { 1621 parse_address((&address), straddr, sizeof(straddr));
1723 host_sin = (struct sockaddr_in *)&host->saddr_in;
1724 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1725
1726 if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) ||
1727 (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1728 if (debug) {
1729 printf("Identical IP already exists. Not adding %s\n", arg);
1730 }
1731 return -1;
1732 }
1733 host = host->next;
1734 }
1735 1622
1736 /* add the fresh ip */ 1623 /* add the fresh ip */
1737 host = (struct rta_host *)malloc(sizeof(struct rta_host)); 1624 ping_target *target = (ping_target *)calloc(1, sizeof(ping_target));
1738 if (!host) { 1625 if (!target) {
1739 char straddr[INET6_ADDRSTRLEN]; 1626 crash("add_target_ip(%s): malloc(%lu) failed", straddr, sizeof(ping_target));
1740 parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr));
1741 crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host));
1742 } 1627 }
1743 memset(host, 0, sizeof(struct rta_host));
1744 1628
1745 /* set the values. use calling name for output */ 1629 ping_target_create_wrapper target_wrapper = ping_target_create(address);
1746 host->name = strdup(arg);
1747 1630
1748 /* fill out the sockaddr_storage struct */ 1631 if (target_wrapper.errorcode == OK) {
1749 if (address_family == AF_INET) { 1632 *target = target_wrapper.host;
1750 host_sin = (struct sockaddr_in *)&host->saddr_in; 1633 result.target = target;
1751 host_sin->sin_family = AF_INET;
1752 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1753 } else { 1634 } else {
1754 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; 1635 result.error_code = target_wrapper.errorcode;
1755 host_sin6->sin6_family = AF_INET6;
1756 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1757 }
1758
1759 /* fill out the sockaddr_in struct */
1760 host->rtmin = INFINITY;
1761 host->rtmax = 0;
1762 host->jitter = 0;
1763 host->jitter_max = 0;
1764 host->jitter_min = INFINITY;
1765 host->last_tdiff = 0;
1766 host->order_status = STATE_OK;
1767 host->last_icmp_seq = 0;
1768 host->rta_status = 0;
1769 host->pl_status = 0;
1770 host->jitter_status = 0;
1771 host->mos_status = 0;
1772 host->score_status = 0;
1773 host->pl_status = 0;
1774
1775 if (!list) {
1776 list = cursor = host;
1777 } else {
1778 cursor->next = host;
1779 } 1636 }
1780 1637
1781 cursor = host; 1638 return result;
1782 targets++;
1783
1784 return 0;
1785} 1639}
1786 1640
1787/* wrapper for add_target_ip */ 1641/* wrapper for add_target_ip */
1788static int add_target(char *arg) { 1642static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode,
1789 int error, result = -1; 1643 sa_family_t enforced_proto) {
1790 struct sockaddr_storage ip; 1644 if (debug > 0) {
1791 struct addrinfo hints, *res, *p; 1645 printf("add_target called with argument %s\n", arg);
1792 struct sockaddr_in *sin; 1646 }
1793 struct sockaddr_in6 *sin6; 1647
1794 1648 struct sockaddr_storage address_storage = {};
1795 switch (address_family) { 1649 struct sockaddr_in *sin = NULL;
1796 case -1: 1650 struct sockaddr_in6 *sin6 = NULL;
1797 /* -4 and -6 are not specified on cmdline */ 1651 int error_code = -1;
1798 address_family = AF_INET; 1652
1799 sin = (struct sockaddr_in *)&ip; 1653 switch (enforced_proto) {
1800 result = inet_pton(address_family, arg, &sin->sin_addr); 1654 case AF_UNSPEC:
1801#ifdef USE_IPV6 1655 /*
1802 if (result != 1) { 1656 * no enforced protocol family
1803 address_family = AF_INET6; 1657 * try to parse the address with each one
1804 sin6 = (struct sockaddr_in6 *)&ip; 1658 */
1805 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1659 sin = (struct sockaddr_in *)&address_storage;
1806 } 1660 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1807#endif 1661 address_storage.ss_family = AF_INET;
1808 /* If we don't find any valid addresses, we still don't know the address_family */ 1662
1809 if (result != 1) { 1663 if (error_code != 1) {
1810 address_family = -1; 1664 sin6 = (struct sockaddr_in6 *)&address_storage;
1665 error_code = inet_pton(AF_INET6, arg, &sin6->sin6_addr);
1666 address_storage.ss_family = AF_INET6;
1811 } 1667 }
1812 break; 1668 break;
1813 case AF_INET: 1669 case AF_INET:
1814 sin = (struct sockaddr_in *)&ip; 1670 sin = (struct sockaddr_in *)&address_storage;
1815 result = inet_pton(address_family, arg, &sin->sin_addr); 1671 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1672 address_storage.ss_family = AF_INET;
1816 break; 1673 break;
1817 case AF_INET6: 1674 case AF_INET6:
1818 sin6 = (struct sockaddr_in6 *)&ip; 1675 sin6 = (struct sockaddr_in6 *)&address_storage;
1819 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1676 error_code = inet_pton(AF_INET, arg, &sin6->sin6_addr);
1677 address_storage.ss_family = AF_INET6;
1820 break; 1678 break;
1821 default: 1679 default:
1822 crash("Address family not supported"); 1680 crash("Address family not supported");
1823 } 1681 }
1824 1682
1825 /* don't resolve if we don't have to */ 1683 add_target_wrapper result = {
1826 if (result == 1) { 1684 .error_code = OK,
1685 .targets = NULL,
1686 .has_v4 = false,
1687 .has_v6 = false,
1688 };
1689
1690 // if error_code == 1 the address was a valid address parsed above
1691 if (error_code == 1) {
1827 /* don't add all ip's if we were given a specific one */ 1692 /* don't add all ip's if we were given a specific one */
1828 return add_target_ip(arg, &ip); 1693 add_target_ip_wrapper targeted = add_target_ip(address_storage);
1829 } else { 1694
1830 errno = 0; 1695 if (targeted.error_code != OK) {
1831 memset(&hints, 0, sizeof(hints)); 1696 result.error_code = ERROR;
1832 if (address_family == -1) { 1697 return result;
1833 hints.ai_family = AF_UNSPEC;
1834 } else {
1835 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1836 } 1698 }
1837 hints.ai_socktype = SOCK_RAW; 1699
1838 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { 1700 if (targeted.target->address.ss_family == AF_INET) {
1839 errno = 0; 1701 result.has_v4 = true;
1840 crash("Failed to resolve %s: %s", arg, gai_strerror(error)); 1702 } else if (targeted.target->address.ss_family == AF_INET6) {
1841 return -1; 1703 result.has_v6 = true;
1704 } else {
1705 assert(false);
1842 } 1706 }
1843 address_family = res->ai_family; 1707 result.targets = targeted.target;
1708 result.number_of_targets = 1;
1709 return result;
1710 }
1711
1712 struct addrinfo hints = {};
1713 errno = 0;
1714 hints.ai_family = enforced_proto;
1715 hints.ai_socktype = SOCK_RAW;
1716
1717 int error;
1718 struct addrinfo *res;
1719 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1720 errno = 0;
1721 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1722 result.error_code = ERROR;
1723 return result;
1844 } 1724 }
1845 1725
1846 /* possibly add all the IP's as targets */ 1726 /* possibly add all the IP's as targets */
1847 for (p = res; p != NULL; p = p->ai_next) { 1727 for (struct addrinfo *address = res; address != NULL; address = address->ai_next) {
1848 memcpy(&ip, p->ai_addr, p->ai_addrlen); 1728 struct sockaddr_storage temporary_ip_address;
1849 add_target_ip(arg, &ip); 1729 memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen);
1730
1731 add_target_ip_wrapper tmp = add_target_ip(temporary_ip_address);
1732
1733 if (tmp.error_code != OK) {
1734 // No proper error handling
1735 // What to do?
1736 } else {
1737 if (result.targets == NULL) {
1738 result.targets = tmp.target;
1739 result.number_of_targets = 1;
1740 } else {
1741 result.number_of_targets += ping_target_list_append(result.targets, tmp.target);
1742 }
1743 if (address->ai_family == AF_INET) {
1744 result.has_v4 = true;
1745 } else if (address->ai_family == AF_INET6) {
1746 result.has_v6 = true;
1747 }
1748 }
1850 1749
1851 /* this is silly, but it works */ 1750 /* this is silly, but it works */
1852 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1751 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1855,18 +1754,20 @@ static int add_target(char *arg) {
1855 } 1754 }
1856 continue; 1755 continue;
1857 } 1756 }
1757
1758 // Abort after first hit if not in of the modes above
1858 break; 1759 break;
1859 } 1760 }
1860 freeaddrinfo(res); 1761 freeaddrinfo(res);
1861 1762
1862 return 0; 1763 return result;
1863} 1764}
1864 1765
1865static void set_source_ip(char *arg) { 1766static void set_source_ip(char *arg, const int icmp_sock, sa_family_t addr_family) {
1866 struct sockaddr_in src; 1767 struct sockaddr_in src;
1867 1768
1868 memset(&src, 0, sizeof(src)); 1769 memset(&src, 0, sizeof(src));
1869 src.sin_family = address_family; 1770 src.sin_family = addr_family;
1870 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) { 1771 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) {
1871 src.sin_addr.s_addr = get_ip_address(arg); 1772 src.sin_addr.s_addr = get_ip_address(arg);
1872 } 1773 }
@@ -1878,8 +1779,8 @@ static void set_source_ip(char *arg) {
1878/* TODO: Move this to netutils.c and also change check_dhcp to use that. */ 1779/* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1879static in_addr_t get_ip_address(const char *ifname) { 1780static in_addr_t get_ip_address(const char *ifname) {
1880 // TODO: Rewrite this so the function return an error and we exit somewhere else 1781 // TODO: Rewrite this so the function return an error and we exit somewhere else
1881 struct sockaddr_in ip; 1782 struct sockaddr_in ip_address;
1882 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy 1783 ip_address.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1883#if defined(SIOCGIFADDR) 1784#if defined(SIOCGIFADDR)
1884 struct ifreq ifr; 1785 struct ifreq ifr;
1885 1786
@@ -1897,7 +1798,7 @@ static in_addr_t get_ip_address(const char *ifname) {
1897 errno = 0; 1798 errno = 0;
1898 crash("Cannot get interface IP address on this platform."); 1799 crash("Cannot get interface IP address on this platform.");
1899#endif 1800#endif
1900 return ip.sin_addr.s_addr; 1801 return ip_address.sin_addr.s_addr;
1901} 1802}
1902 1803
1903/* 1804/*
@@ -1906,103 +1807,127 @@ static in_addr_t get_ip_address(const char *ifname) {
1906 * s = seconds 1807 * s = seconds
1907 * return value is in microseconds 1808 * return value is in microseconds
1908 */ 1809 */
1909static u_int get_timevar(const char *str) { 1810static get_timevar_wrapper get_timevar(const char *str) {
1910 char p, u, *ptr; 1811 get_timevar_wrapper result = {
1911 size_t len; 1812 .error_code = OK,
1912 u_int i, d; /* integer and decimal, respectively */ 1813 .time_range = 0,
1913 u_int factor = 1000; /* default to milliseconds */ 1814 };
1914 1815
1915 if (!str) { 1816 if (!str) {
1916 return 0; 1817 result.error_code = ERROR;
1818 return result;
1917 } 1819 }
1918 len = strlen(str); 1820
1821 size_t len = strlen(str);
1919 if (!len) { 1822 if (!len) {
1920 return 0; 1823 result.error_code = ERROR;
1824 return result;
1921 } 1825 }
1922 1826
1923 /* unit might be given as ms|m (millisec), 1827 /* unit might be given as ms|m (millisec),
1924 * us|u (microsec) or just plain s, for seconds */ 1828 * us|u (microsec) or just plain s, for seconds */
1925 p = '\0'; 1829 char tmp = '\0';
1926 u = str[len - 1]; 1830 char unit = str[len - 1];
1927 if (len >= 2 && !isdigit((int)str[len - 2])) { 1831 if (len >= 2 && !isdigit((int)str[len - 2])) {
1928 p = str[len - 2]; 1832 tmp = str[len - 2];
1929 } 1833 }
1930 if (p && u == 's') { 1834
1931 u = p; 1835 if (tmp && unit == 's') {
1932 } else if (!p) { 1836 unit = tmp;
1933 p = u; 1837 } else if (!tmp) {
1838 tmp = unit;
1934 } 1839 }
1840
1935 if (debug > 2) { 1841 if (debug > 2) {
1936 printf("evaluating %s, u: %c, p: %c\n", str, u, p); 1842 printf("evaluating %s, u: %c, p: %c\n", str, unit, tmp);
1937 } 1843 }
1938 1844
1939 if (u == 'u') { 1845 unsigned int factor = 1000; /* default to milliseconds */
1846 if (unit == 'u') {
1940 factor = 1; /* microseconds */ 1847 factor = 1; /* microseconds */
1941 } else if (u == 'm') { 1848 } else if (unit == 'm') {
1942 factor = 1000; /* milliseconds */ 1849 factor = 1000; /* milliseconds */
1943 } else if (u == 's') { 1850 } else if (unit == 's') {
1944 factor = 1000000; /* seconds */ 1851 factor = 1000000; /* seconds */
1945 } 1852 }
1853
1946 if (debug > 2) { 1854 if (debug > 2) {
1947 printf("factor is %u\n", factor); 1855 printf("factor is %u\n", factor);
1948 } 1856 }
1949 1857
1950 i = strtoul(str, &ptr, 0); 1858 char *ptr;
1859 unsigned long pre_radix;
1860 pre_radix = strtoul(str, &ptr, 0);
1951 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { 1861 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) {
1952 return i * factor; 1862 result.time_range = (unsigned int)(pre_radix * factor);
1863 return result;
1953 } 1864 }
1954 1865
1955 /* time specified in usecs can't have decimal points, so ignore them */ 1866 /* time specified in usecs can't have decimal points, so ignore them */
1956 if (factor == 1) { 1867 if (factor == 1) {
1957 return i; 1868 result.time_range = (unsigned int)pre_radix;
1869 return result;
1958 } 1870 }
1959 1871
1960 d = strtoul(ptr + 1, NULL, 0); 1872 /* integer and decimal, respectively */
1873 unsigned int post_radix = (unsigned int)strtoul(ptr + 1, NULL, 0);
1961 1874
1962 /* d is decimal, so get rid of excess digits */ 1875 /* d is decimal, so get rid of excess digits */
1963 while (d >= factor) { 1876 while (post_radix >= factor) {
1964 d /= 10; 1877 post_radix /= 10;
1965 } 1878 }
1966 1879
1967 /* the last parenthesis avoids floating point exceptions. */ 1880 /* the last parenthesis avoids floating point exceptions. */
1968 return ((i * factor) + (d * (factor / 10))); 1881 result.time_range = (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10)));
1882 return result;
1969} 1883}
1970 1884
1971/* not too good at checking errors, but it'll do (main() should barfe on -1) */ 1885static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) {
1972static int get_threshold(char *str, threshold *th) { 1886 get_threshold_wrapper result = {
1973 char *p = NULL, i = 0; 1887 .errorcode = OK,
1888 .threshold = threshold,
1889 };
1974 1890
1975 if (!str || !strlen(str) || !th) { 1891 if (!str || !strlen(str)) {
1976 return -1; 1892 result.errorcode = ERROR;
1893 return result;
1977 } 1894 }
1978 1895
1979 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ 1896 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1980 p = &str[strlen(str) - 1]; 1897 bool is_at_last_char = false;
1981 while (p != &str[1]) { 1898 char *tmp = &str[strlen(str) - 1];
1982 if (*p == '%') { 1899 while (tmp != &str[1]) {
1983 *p = '\0'; 1900 if (*tmp == '%') {
1984 } else if (*p == ',' && i) { 1901 *tmp = '\0';
1985 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1902 } else if (*tmp == ',' && is_at_last_char) {
1986 th->pl = (unsigned char)strtoul(p + 1, NULL, 0); 1903 *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */
1904 result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0);
1987 break; 1905 break;
1988 } 1906 }
1989 i = 1; 1907 is_at_last_char = true;
1990 p--; 1908 tmp--;
1991 } 1909 }
1992 th->rta = get_timevar(str);
1993 1910
1994 if (!th->rta) { 1911 get_timevar_wrapper parsed_time = get_timevar(str);
1995 return -1; 1912
1913 if (parsed_time.error_code == OK) {
1914 result.threshold.rta = parsed_time.time_range;
1915 } else {
1916 if (debug > 1) {
1917 printf("%s: failed to parse rta threshold\n", __FUNCTION__);
1918 }
1919 result.errorcode = ERROR;
1920 return result;
1996 } 1921 }
1997 1922
1998 if (th->rta > MAXTTL * 1000000) { 1923 if (result.threshold.rta > MAXTTL * 1000000) {
1999 th->rta = MAXTTL * 1000000; 1924 result.threshold.rta = MAXTTL * 1000000;
2000 } 1925 }
2001 if (th->pl > 100) { 1926 if (result.threshold.pl > 100) {
2002 th->pl = 100; 1927 result.threshold.pl = 100;
2003 } 1928 }
2004 1929
2005 return 0; 1930 return result;
2006} 1931}
2007 1932
2008/* 1933/*
@@ -2013,184 +1938,537 @@ static int get_threshold(char *str, threshold *th) {
2013 * @param[in] length strlen(str) 1938 * @param[in] length strlen(str)
2014 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned 1939 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
2015 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned 1940 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
2016 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively) 1941 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score
1942 * (exclusively)
2017 */ 1943 */
2018static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { 1944static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
2019 if (!str || !length || !warn || !crit) { 1945 check_icmp_threshold crit, threshold_mode mode) {
2020 return false; 1946 get_threshold2_wrapper result = {
1947 .errorcode = OK,
1948 .warn = warn,
1949 .crit = crit,
1950 };
1951
1952 if (!str || !length) {
1953 result.errorcode = ERROR;
1954 return result;
2021 } 1955 }
2022 1956
2023 // p points to the last char in str 1957 // p points to the last char in str
2024 char *p = &str[length - 1]; 1958 char *work_pointer = &str[length - 1];
2025 1959
2026 // first_iteration is bof-stop on stupid libc's 1960 // first_iteration is bof-stop on stupid libc's
2027 bool first_iteration = true; 1961 bool first_iteration = true;
2028 1962
2029 while (p != &str[0]) { 1963 while (work_pointer != &str[0]) {
2030 if ((*p == 'm') || (*p == '%')) { 1964 if ((*work_pointer == 'm') || (*work_pointer == '%')) {
2031 *p = '\0'; 1965 *work_pointer = '\0';
2032 } else if (*p == ',' && !first_iteration) { 1966 } else if (*work_pointer == ',' && !first_iteration) {
2033 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1967 *work_pointer = '\0'; /* reset it so get_timevar(str) works nicely later */
2034 1968
2035 char *start_of_value = p + 1; 1969 char *start_of_value = work_pointer + 1;
2036 1970
2037 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { 1971 parse_threshold2_helper_wrapper tmp =
2038 return false; 1972 parse_threshold2_helper(start_of_value, strlen(start_of_value), result.crit, mode);
1973 if (tmp.errorcode != OK) {
1974 result.errorcode = ERROR;
1975 return result;
2039 } 1976 }
1977 result.crit = tmp.result;
2040 } 1978 }
2041 first_iteration = false; 1979 first_iteration = false;
2042 p--; 1980 work_pointer--;
2043 } 1981 }
2044 1982
2045 return parse_threshold2_helper(p, strlen(p), warn, mode); 1983 parse_threshold2_helper_wrapper tmp =
1984 parse_threshold2_helper(work_pointer, strlen(work_pointer), result.warn, mode);
1985 if (tmp.errorcode != OK) {
1986 result.errorcode = ERROR;
1987 } else {
1988 result.warn = tmp.result;
1989 }
1990 return result;
2046} 1991}
2047 1992
2048static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { 1993static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
1994 size_t length,
1995 check_icmp_threshold thr,
1996 threshold_mode mode) {
2049 char *resultChecker = {0}; 1997 char *resultChecker = {0};
1998 parse_threshold2_helper_wrapper result = {
1999 .result = thr,
2000 .errorcode = OK,
2001 };
2050 2002
2051 switch (mode) { 2003 switch (mode) {
2052 case const_rta_mode: 2004 case const_rta_mode:
2053 thr->rta = strtod(s, &resultChecker) * 1000; 2005 result.result.rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000);
2054 break; 2006 break;
2055 case const_packet_loss_mode: 2007 case const_packet_loss_mode:
2056 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0); 2008 result.result.pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0);
2057 break; 2009 break;
2058 case const_jitter_mode: 2010 case const_jitter_mode:
2059 thr->jitter = strtod(s, &resultChecker); 2011 result.result.jitter = strtod(threshold_string, &resultChecker);
2060
2061 break; 2012 break;
2062 case const_mos_mode: 2013 case const_mos_mode:
2063 thr->mos = strtod(s, &resultChecker); 2014 result.result.mos = strtod(threshold_string, &resultChecker);
2064 break; 2015 break;
2065 case const_score_mode: 2016 case const_score_mode:
2066 thr->score = strtod(s, &resultChecker); 2017 result.result.score = strtod(threshold_string, &resultChecker);
2067 break; 2018 break;
2068 } 2019 }
2069 2020
2070 if (resultChecker == s) { 2021 if (resultChecker == threshold_string) {
2071 // Failed to parse 2022 // Failed to parse
2072 return false; 2023 result.errorcode = ERROR;
2024 return result;
2073 } 2025 }
2074 2026
2075 if (resultChecker != (s + length)) { 2027 if (resultChecker != (threshold_string + length)) {
2076 // Trailing symbols 2028 // Trailing symbols
2077 return false; 2029 result.errorcode = ERROR;
2078 } 2030 }
2079 2031
2080 return true; 2032 return result;
2081} 2033}
2082 2034
2083unsigned short icmp_checksum(uint16_t *p, size_t n) { 2035unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) {
2084 unsigned short cksum;
2085 long sum = 0; 2036 long sum = 0;
2086 2037
2087 /* sizeof(uint16_t) == 2 */ 2038 /* sizeof(uint16_t) == 2 */
2088 while (n >= 2) { 2039 while (packet_size >= 2) {
2089 sum += *(p++); 2040 sum += *(packet++);
2090 n -= 2; 2041 packet_size -= 2;
2091 } 2042 }
2092 2043
2093 /* mop up the occasional odd byte */ 2044 /* mop up the occasional odd byte */
2094 if (n == 1) { 2045 if (packet_size == 1) {
2095 sum += *((uint8_t *)p - 1); 2046 sum += *((uint8_t *)packet - 1);
2096 } 2047 }
2097 2048
2098 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 2049 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2099 sum += (sum >> 16); /* add carry */ 2050 sum += (sum >> 16); /* add carry */
2100 cksum = ~sum; /* ones-complement, trunc to 16 bits */ 2051 unsigned short cksum;
2052 cksum = (unsigned short)~sum; /* ones-complement, trunc to 16 bits */
2101 2053
2102 return cksum; 2054 return cksum;
2103} 2055}
2104 2056
2105void print_help(void) { 2057void print_help(void) {
2106 /*print_revision (progname);*/ /* FIXME: Why? */ 2058 // print_revision (progname); /* FIXME: Why? */
2107 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n"); 2059 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2108 2060
2109 printf(COPYRIGHT, copyright, email); 2061 printf(COPYRIGHT, copyright, email);
2110 2062
2111 printf("\n\n");
2112
2113 print_usage(); 2063 print_usage();
2114 2064
2115 printf(UT_HELP_VRSN); 2065 printf(UT_HELP_VRSN);
2116 printf(UT_EXTRA_OPTS); 2066 printf(UT_EXTRA_OPTS);
2117 2067
2118 printf(" %s\n", "-H"); 2068 printf(" -H, --Host=HOST\n");
2119 printf(" %s\n", _("specify a target")); 2069 printf(" %s\n",
2120 printf(" %s\n", "[-4|-6]"); 2070 _("specify a target, might be one of: resolveable name | IPv6 address | IPv4 address\n"
2121 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); 2071 " (required, can be given multiple times)"));
2122 printf(" %s\n", "-w"); 2072 printf(" %s\n", "[-4|-6], [--ipv4-only|--ipv6-only]");
2123 printf(" %s", _("warning threshold (currently ")); 2073 printf(" %s\n", _("Use IPv4 or IPv6 only to communicate with the targets"));
2124 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 2074 printf(" %s\n", "-w, --warning=WARN_VALUE");
2125 printf(" %s\n", "-c"); 2075 printf(" %s", _("warning threshold (default "));
2126 printf(" %s", _("critical threshold (currently ")); 2076 printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL);
2127 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); 2077 printf(" %s\n", "-c, --critical=CRIT_VALUE");
2128 2078 printf(" %s", _("critical threshold (default "));
2129 printf(" %s\n", "-R"); 2079 printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL);
2130 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); 2080
2131 printf(" %s\n", "-P"); 2081 printf(" %s\n", "-R, --rta-mode-thresholds=RTA_THRESHOLDS");
2082 printf(" %s\n",
2083 _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms"));
2084 printf(" %s\n", "-P, --packet-loss-mode-thresholds=PACKET_LOSS_THRESHOLD");
2132 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); 2085 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2133 printf(" %s\n", "-J"); 2086 printf(" %s\n", "-J, --jitter-mode-thresholds=JITTER_MODE_THRESHOLD");
2134 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms ")); 2087 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2135 printf(" %s\n", "-M"); 2088 printf(" %s\n", "-M, --mos-mode-thresholds=MOS_MODE_THRESHOLD");
2136 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0")); 2089 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2137 printf(" %s\n", "-S"); 2090 printf(" %s\n", "-S, --score-mode-thresholds=SCORE_MODE_THRESHOLD");
2138 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 ")); 2091 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2139 printf(" %s\n", "-O"); 2092 printf(" %s\n", "-O, --out-of-order-packets");
2140 printf(" %s\n", _("detect out of order ICMP packts ")); 2093 printf(
2141 printf(" %s\n", "-H"); 2094 " %s\n",
2142 printf(" %s\n", _("specify a target")); 2095 _("detect out of order ICMP packets, if such packets are found, the result is CRITICAL"));
2143 printf(" %s\n", "-s"); 2096 printf(" %s\n", "[-n|-p], --number-of-packets=NUMBER_OF_PACKETS");
2144 printf(" %s\n", _("specify a source IP address or device name")); 2097 printf(" %s", _("number of packets to send (default "));
2145 printf(" %s\n", "-n"); 2098 printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS);
2146 printf(" %s", _("number of packets to send (currently ")); 2099
2147 printf("%u)\n", packets);
2148 printf(" %s\n", "-p");
2149 printf(" %s", _("number of packets to send (currently "));
2150 printf("%u)\n", packets);
2151 printf(" %s\n", "-i"); 2100 printf(" %s\n", "-i");
2152 printf(" %s", _("max packet interval (currently ")); 2101 printf(" %s", _("[DEPRECATED] packet interval (default "));
2153 printf("%0.3fms)\n", (float)pkt_interval / 1000); 2102 printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000);
2154 printf(" %s\n", "-I"); 2103 printf(" %s", _("This option was never actually used and is just mentioned here for "
2155 printf(" %s", _("max target interval (currently ")); 2104 "historical purposes\n"));
2156 printf("%0.3fms)\n", (float)target_interval / 1000); 2105
2157 printf(" %s\n", "-m"); 2106 printf(" %s\n", "-I, --target-interval=TARGET_INTERVAL");
2158 printf(" %s", _("number of alive hosts required for success")); 2107 printf(" %s%0.3fms)\n The time interval to wait in between one target and the next\n",
2108 _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000);
2109 printf(" %s\n", "-m, --minimal-host-alive=MIN_ALIVE");
2110 printf(" %s", _("number of alive hosts required for success. If less than MIN_ALIVE hosts "
2111 "are OK, but MIN_ALIVE hosts are WARNING or OK, WARNING, else CRITICAL"));
2159 printf("\n"); 2112 printf("\n");
2160 printf(" %s\n", "-l"); 2113 printf(" %s\n", "-l, --outgoing-ttl=OUTGOING_TTL");
2161 printf(" %s", _("TTL on outgoing packets (currently ")); 2114 printf(" %s", _("TTL on outgoing packets (default "));
2162 printf("%u)\n", ttl); 2115 printf("%u)\n", DEFAULT_TTL);
2163 printf(" %s\n", "-t"); 2116 printf(" %s\n", "-b, --size=SIZE");
2164 printf(" %s", _("timeout value (seconds, currently ")); 2117 printf(" %s\n", _("Number of icmp ping data bytes to send"));
2165 printf("%u)\n", timeout); 2118 printf(" %s %lu + %d)\n", _("Packet size will be SIZE + icmp header (default"),
2166 printf(" %s\n", "-b"); 2119 DEFAULT_PING_DATA_SIZE, ICMP_MINLEN);
2167 printf(" %s\n", _("Number of icmp data bytes to send")); 2120 printf(" %s\n", "-v, --verbose");
2168 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN); 2121 printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)"));
2169 printf(" %s\n", "-v"); 2122
2170 printf(" %s\n", _("verbose")); 2123 printf(UT_OUTPUT_FORMAT);
2124
2171 printf("\n"); 2125 printf("\n");
2172 printf("%s\n", _("Notes:")); 2126 printf("%s\n", _("Notes:"));
2173 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P")); 2127 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2174 printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); 2128 printf(" %s\n", _("Naming a host (or several) to check is not."));
2175 printf("\n"); 2129 printf("\n");
2176 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); 2130 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2177 printf(" %s\n", _("packet loss. The default values should work well for most users.")); 2131 printf(" %s\n", _("packet loss. The default values should work well for most users."));
2178 printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations")); 2132 printf(" %s\n",
2179 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); 2133 _("You can specify different RTA factors using the standardized abbreviations"));
2180 /* -d not yet implemented */ 2134 printf(" %s\n",
2181 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); 2135 _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2182 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2183 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2184 printf("\n");
2185 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2186 /* printf ("%s\n", _("Long options are currently unsupported."));
2187 printf ("%s\n", _("Options marked with * require an argument"));
2188 */
2189 2136
2190 printf(UT_SUPPORT); 2137 printf(UT_SUPPORT);
2191} 2138}
2192 2139
2193void print_usage(void) { 2140void print_usage(void) {
2194 printf("%s\n", _("Usage:")); 2141 printf("%s\n", _("Usage:"));
2195 printf(" %s [options] [-H] host1 host2 hostN\n", progname); 2142 printf(" %s [options] [-H host1 [-H host2 [-H hostN]]]\n", progname);
2143}
2144
2145static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
2146 sa_family_t enforced_proto) {
2147 if (debug) {
2148 printf("add_host called with argument %s\n", arg);
2149 }
2150
2151 add_host_wrapper result = {
2152 .error_code = OK,
2153 .host = check_icmp_target_container_init(),
2154 .has_v4 = false,
2155 .has_v6 = false,
2156 };
2157
2158 add_target_wrapper targets = add_target(arg, mode, enforced_proto);
2159
2160 if (targets.error_code != OK) {
2161 result.error_code = targets.error_code;
2162 return result;
2163 }
2164
2165 result.has_v4 = targets.has_v4;
2166 result.has_v6 = targets.has_v6;
2167
2168 result.host = check_icmp_target_container_init();
2169
2170 result.host.name = strdup(arg);
2171 result.host.target_list = targets.targets;
2172 result.host.number_of_targets = targets.number_of_targets;
2173
2174 return result;
2175}
2176
2177mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
2178 check_icmp_threshold warn, check_icmp_threshold crit) {
2179 /* if no new mode selected, use old schema */
2180 if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode &&
2181 !modes.mos_mode && !modes.order_mode) {
2182 modes.rta_mode = true;
2183 modes.pl_mode = true;
2184 }
2185
2186 mp_subcheck result = mp_subcheck_init();
2187 result = mp_set_subcheck_default_state(result, STATE_OK);
2188
2189 char address[INET6_ADDRSTRLEN];
2190 memset(address, 0, INET6_ADDRSTRLEN);
2191 parse_address(&target.address, address, sizeof(address));
2192
2193 xasprintf(&result.output, "%s", address);
2194
2195 double packet_loss;
2196 time_t rta;
2197 if (!target.icmp_recv) {
2198 /* rta 0 is of course not entirely correct, but will still show up
2199 * conspicuously as missing entries in perfparse and cacti */
2200 packet_loss = 100;
2201 rta = 0;
2202 result = mp_set_subcheck_state(result, STATE_CRITICAL);
2203 /* up the down counter if not already counted */
2204
2205 if (target.flags & FLAG_LOST_CAUSE) {
2206 xasprintf(&result.output, "%s: %s @ %s", result.output,
2207 get_icmp_error_msg(target.icmp_type, target.icmp_code), address);
2208 } else { /* not marked as lost cause, so we have no flags for it */
2209 xasprintf(&result.output, "%s", result.output);
2210 }
2211 } else {
2212 packet_loss =
2213 (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent;
2214 rta = target.time_waited / target.icmp_recv;
2215 }
2216
2217 double EffectiveLatency;
2218 double mos; /* Mean opinion score */
2219 double score; /* score */
2220
2221 if (target.icmp_recv > 1) {
2222 /*
2223 * This algorithm is probably pretty much blindly copied from
2224 * locations like this one:
2225 * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS
2226 * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick
2227 * research MOS originates from the Audio/Video transport network area. Whether it can
2228 * and should be computed from ICMP data, I can not say.
2229 *
2230 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
2231 *
2232 * MOS stands likely for Mean Opinion Score (
2233 * https://en.wikipedia.org/wiki/Mean_Opinion_Score )
2234 *
2235 * More links:
2236 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
2237 */
2238 target.jitter = (target.jitter / (target.icmp_recv - 1) / 1000);
2239
2240 /*
2241 * Take the average round trip latency (in milliseconds), add
2242 * round trip jitter, but double the impact to latency
2243 * then add 10 for protocol latencies (in milliseconds).
2244 */
2245 EffectiveLatency = ((double)rta / 1000) + target.jitter * 2 + 10;
2246
2247 double R;
2248 if (EffectiveLatency < 160) {
2249 R = 93.2 - (EffectiveLatency / 40);
2250 } else {
2251 R = 93.2 - ((EffectiveLatency - 120) / 10);
2252 }
2253
2254 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
2255 // loss of 5% will be entered as 5).
2256 R = R - (packet_loss * 2.5);
2257
2258 if (R < 0) {
2259 R = 0;
2260 }
2261
2262 score = R;
2263 mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
2264 } else {
2265 target.jitter = 0;
2266 target.jitter_min = 0;
2267 target.jitter_max = 0;
2268 mos = 0;
2269 }
2270
2271 /* Check which mode is on and do the warn / Crit stuff */
2272 if (modes.rta_mode) {
2273 mp_subcheck sc_rta = mp_subcheck_init();
2274 sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK);
2275 xasprintf(&sc_rta.output, "rta %0.3fms", (double)rta / 1000);
2276
2277 if (rta >= crit.rta) {
2278 sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL);
2279 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)crit.rta / 1000);
2280 } else if (rta >= warn.rta) {
2281 sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING);
2282 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)warn.rta / 1000);
2283 }
2284
2285 if (packet_loss < 100) {
2286 mp_perfdata pd_rta = perfdata_init();
2287 xasprintf(&pd_rta.label, "%srta", address);
2288 pd_rta.uom = strdup("ms");
2289 pd_rta.value = mp_create_pd_value(rta / 1000);
2290 pd_rta.min = mp_create_pd_value(0);
2291
2292 pd_rta.warn = mp_range_set_end(pd_rta.warn, mp_create_pd_value(warn.rta));
2293 pd_rta.crit = mp_range_set_end(pd_rta.crit, mp_create_pd_value(crit.rta));
2294 mp_add_perfdata_to_subcheck(&sc_rta, pd_rta);
2295
2296 mp_perfdata pd_rt_min = perfdata_init();
2297 xasprintf(&pd_rt_min.label, "%srtmin", address);
2298 pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000);
2299 pd_rt_min.uom = strdup("ms");
2300 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min);
2301
2302 mp_perfdata pd_rt_max = perfdata_init();
2303 xasprintf(&pd_rt_max.label, "%srtmax", address);
2304 pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000);
2305 pd_rt_max.uom = strdup("ms");
2306 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max);
2307 }
2308
2309 mp_add_subcheck_to_subcheck(&result, sc_rta);
2310 }
2311
2312 if (modes.pl_mode) {
2313 mp_subcheck sc_pl = mp_subcheck_init();
2314 sc_pl = mp_set_subcheck_default_state(sc_pl, STATE_OK);
2315 xasprintf(&sc_pl.output, "packet loss %.1f%%", packet_loss);
2316
2317 if (packet_loss >= crit.pl) {
2318 sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL);
2319 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, crit.pl);
2320 } else if (packet_loss >= warn.pl) {
2321 sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING);
2322 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, warn.pl);
2323 }
2324
2325 mp_perfdata pd_pl = perfdata_init();
2326 xasprintf(&pd_pl.label, "%spl", address);
2327 pd_pl.uom = strdup("%");
2328
2329 pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl));
2330 pd_pl.crit = mp_range_set_end(pd_pl.crit, mp_create_pd_value(crit.pl));
2331 pd_pl.value = mp_create_pd_value(packet_loss);
2332
2333 mp_add_perfdata_to_subcheck(&sc_pl, pd_pl);
2334
2335 mp_add_subcheck_to_subcheck(&result, sc_pl);
2336 }
2337
2338 if (modes.jitter_mode) {
2339 mp_subcheck sc_jitter = mp_subcheck_init();
2340 sc_jitter = mp_set_subcheck_default_state(sc_jitter, STATE_OK);
2341 xasprintf(&sc_jitter.output, "jitter %0.3fms", target.jitter);
2342
2343 if (target.jitter >= crit.jitter) {
2344 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL);
2345 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, crit.jitter);
2346 } else if (target.jitter >= warn.jitter) {
2347 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING);
2348 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, warn.jitter);
2349 }
2350
2351 if (packet_loss < 100) {
2352 mp_perfdata pd_jitter = perfdata_init();
2353 pd_jitter.uom = strdup("ms");
2354 xasprintf(&pd_jitter.label, "%sjitter_avg", address);
2355 pd_jitter.value = mp_create_pd_value(target.jitter);
2356 pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter));
2357 pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter));
2358 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
2359
2360 mp_perfdata pd_jitter_min = perfdata_init();
2361 pd_jitter_min.uom = strdup("ms");
2362 xasprintf(&pd_jitter_min.label, "%sjitter_min", address);
2363 pd_jitter_min.value = mp_create_pd_value(target.jitter_min);
2364 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min);
2365
2366 mp_perfdata pd_jitter_max = perfdata_init();
2367 pd_jitter_max.uom = strdup("ms");
2368 xasprintf(&pd_jitter_max.label, "%sjitter_max", address);
2369 pd_jitter_max.value = mp_create_pd_value(target.jitter_max);
2370 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max);
2371 }
2372 mp_add_subcheck_to_subcheck(&result, sc_jitter);
2373 }
2374
2375 if (modes.mos_mode) {
2376 mp_subcheck sc_mos = mp_subcheck_init();
2377 sc_mos = mp_set_subcheck_default_state(sc_mos, STATE_OK);
2378 xasprintf(&sc_mos.output, "MOS %0.1f", mos);
2379
2380 if (mos <= crit.mos) {
2381 sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL);
2382 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, crit.mos);
2383 } else if (mos <= warn.mos) {
2384 sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING);
2385 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, warn.mos);
2386 }
2387
2388 if (packet_loss < 100) {
2389 mp_perfdata pd_mos = perfdata_init();
2390 xasprintf(&pd_mos.label, "%smos", address);
2391 pd_mos.value = mp_create_pd_value(mos);
2392 pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos));
2393 pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos));
2394 pd_mos.min = mp_create_pd_value(0); // MOS starts at 0
2395 pd_mos.max = mp_create_pd_value(5); // MOS max is 5, by definition
2396 mp_add_perfdata_to_subcheck(&sc_mos, pd_mos);
2397 }
2398 mp_add_subcheck_to_subcheck(&result, sc_mos);
2399 }
2400
2401 if (modes.score_mode) {
2402 mp_subcheck sc_score = mp_subcheck_init();
2403 sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK);
2404 xasprintf(&sc_score.output, "Score %f", score);
2405
2406 if (score <= crit.score) {
2407 sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL);
2408 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, crit.score);
2409 } else if (score <= warn.score) {
2410 sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING);
2411 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, warn.score);
2412 }
2413
2414 if (packet_loss < 100) {
2415 mp_perfdata pd_score = perfdata_init();
2416 xasprintf(&pd_score.label, "%sscore", address);
2417 pd_score.value = mp_create_pd_value(score);
2418 pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score));
2419 pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score));
2420 pd_score.min = mp_create_pd_value(0);
2421 pd_score.max = mp_create_pd_value(100);
2422 mp_add_perfdata_to_subcheck(&sc_score, pd_score);
2423 }
2424
2425 mp_add_subcheck_to_subcheck(&result, sc_score);
2426 }
2427
2428 if (modes.order_mode) {
2429 mp_subcheck sc_order = mp_subcheck_init();
2430 sc_order = mp_set_subcheck_default_state(sc_order, STATE_OK);
2431
2432 if (target.found_out_of_order_packets) {
2433 mp_set_subcheck_state(sc_order, STATE_CRITICAL);
2434 xasprintf(&sc_order.output, "Packets out of order");
2435 } else {
2436 xasprintf(&sc_order.output, "Packets in order");
2437 }
2438
2439 mp_add_subcheck_to_subcheck(&result, sc_order);
2440 }
2441
2442 return result;
2443}
2444
2445evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
2446 check_icmp_mode_switches modes, check_icmp_threshold warn,
2447 check_icmp_threshold crit) {
2448 evaluate_host_wrapper result = {
2449 .targets_warn = 0,
2450 .targets_ok = 0,
2451 .sc_host = mp_subcheck_init(),
2452 };
2453 result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK);
2454
2455 result.sc_host.output = strdup(host.name);
2456
2457 ping_target *target = host.target_list;
2458 for (unsigned int i = 0; i < host.number_of_targets; i++) {
2459 mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit);
2460
2461 mp_state_enum target_state = mp_compute_subcheck_state(sc_target);
2462
2463 if (target_state == STATE_WARNING) {
2464 result.targets_warn++;
2465 } else if (target_state == STATE_OK) {
2466 result.targets_ok++;
2467 }
2468 mp_add_subcheck_to_subcheck(&result.sc_host, sc_target);
2469
2470 target = target->next;
2471 }
2472
2473 return result;
2196} 2474}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c
new file mode 100644
index 00000000..d56fbd8b
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.c
@@ -0,0 +1,134 @@
1#include "./config.h"
2#include <math.h>
3#include <netinet/in.h>
4#include <sys/socket.h>
5#include "./check_icmp_helpers.h"
6#include "../../plugins/netutils.h"
7
8// timeout as a global variable to make it available to the timeout handler
9unsigned int timeout = DEFAULT_TIMEOUT;
10
11check_icmp_config check_icmp_config_init() {
12 check_icmp_config tmp = {
13 .modes =
14 {
15 .order_mode = false,
16 .mos_mode = false,
17 .rta_mode = false,
18 .pl_mode = false,
19 .jitter_mode = false,
20 .score_mode = false,
21 },
22
23 .min_hosts_alive = -1,
24 .crit = {.pl = DEFAULT_CRIT_PL,
25 .rta = DEFAULT_CRIT_RTA,
26 .jitter = 50.0,
27 .mos = 3.0,
28 .score = 70.0},
29 .warn = {.pl = DEFAULT_WARN_PL,
30 .rta = DEFAULT_WARN_RTA,
31 .jitter = 40.0,
32 .mos = 3.5,
33 .score = 80.0},
34
35 .ttl = DEFAULT_TTL,
36 .icmp_data_size = DEFAULT_PING_DATA_SIZE,
37 .target_interval = 0,
38 .number_of_packets = DEFAULT_NUMBER_OF_PACKETS,
39
40 .source_ip = NULL,
41 .need_v4 = false,
42 .need_v6 = false,
43
44 .sender_id = 0,
45
46 .mode = MODE_RTA,
47
48 .number_of_targets = 0,
49 .targets = NULL,
50
51 .number_of_hosts = 0,
52 .hosts = NULL,
53
54 .output_format_is_set = false,
55 };
56 return tmp;
57}
58
59ping_target ping_target_init() {
60 ping_target tmp = {
61 .rtmin = INFINITY,
62
63 .jitter_min = INFINITY,
64
65 .found_out_of_order_packets = false,
66 };
67
68 return tmp;
69}
70
71check_icmp_state check_icmp_state_init() {
72 check_icmp_state tmp = {.icmp_sent = 0, .icmp_lost = 0, .icmp_recv = 0, .targets_down = 0};
73
74 return tmp;
75}
76
77ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) {
78 ping_target_create_wrapper result = {
79 .errorcode = OK,
80 };
81
82 struct sockaddr_storage *tmp_addr = &address;
83
84 /* disregard obviously stupid addresses
85 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
86 if (((tmp_addr->ss_family == AF_INET &&
87 (((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_NONE ||
88 ((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) ||
89 (tmp_addr->ss_family == AF_INET6 &&
90 (((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
91 result.errorcode = ERROR;
92 return result;
93 }
94
95 /* add the fresh ip */
96 ping_target target = ping_target_init();
97
98 /* fill out the sockaddr_storage struct */
99 target.address = address;
100
101 result.host = target;
102
103 return result;
104}
105
106check_icmp_target_container check_icmp_target_container_init() {
107 check_icmp_target_container tmp = {
108 .name = NULL,
109 .number_of_targets = 0,
110 .target_list = NULL,
111 };
112 return tmp;
113}
114
115unsigned int ping_target_list_append(ping_target *list, ping_target *elem) {
116 if (elem == NULL || list == NULL) {
117 return 0;
118 }
119
120 while (list->next != NULL) {
121 list = list->next;
122 }
123
124 list->next = elem;
125
126 unsigned int result = 1;
127
128 while (elem->next != NULL) {
129 result++;
130 elem = elem->next;
131 }
132
133 return result;
134}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h
new file mode 100644
index 00000000..dc6ea40b
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.h
@@ -0,0 +1,68 @@
1#pragma once
2
3#include "../../lib/states.h"
4#include <netinet/in_systm.h>
5#include <netinet/in.h>
6#include <netinet/ip.h>
7#include <netinet/ip6.h>
8#include <netinet/ip_icmp.h>
9#include <netinet/icmp6.h>
10#include <arpa/inet.h>
11
12typedef struct ping_target {
13 unsigned short id; /* id in **table, and icmp pkts */
14 char *msg; /* icmp error message, if any */
15
16 struct sockaddr_storage address; /* the address of this host */
17 struct sockaddr_storage error_addr; /* stores address of error replies */
18 time_t time_waited; /* total time waited, in usecs */
19 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
20 unsigned char icmp_type, icmp_code; /* type and code from errors */
21 unsigned short flags; /* control/status flags */
22
23 double rtmax; /* max rtt */
24 double rtmin; /* min rtt */
25
26 double jitter; /* measured jitter */
27 double jitter_max; /* jitter rtt maximum */
28 double jitter_min; /* jitter rtt minimum */
29
30 time_t last_tdiff;
31 unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
32
33 bool found_out_of_order_packets;
34
35 struct ping_target *next;
36} ping_target;
37
38ping_target ping_target_init();
39
40typedef struct {
41 char *name;
42 ping_target *target_list;
43 unsigned int number_of_targets;
44} check_icmp_target_container;
45
46check_icmp_target_container check_icmp_target_container_init();
47
48typedef struct {
49 unsigned int icmp_sent;
50 unsigned int icmp_recv;
51 unsigned int icmp_lost;
52 unsigned short targets_down;
53} check_icmp_state;
54
55check_icmp_state check_icmp_state_init();
56
57typedef struct {
58 int errorcode;
59 ping_target host;
60} ping_target_create_wrapper;
61
62typedef struct {
63 int socket4;
64 int socket6;
65} check_icmp_socket_set;
66
67ping_target_create_wrapper ping_target_create(struct sockaddr_storage address);
68unsigned int ping_target_list_append(ping_target *list, ping_target *elem);
diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h
new file mode 100644
index 00000000..c348bef5
--- /dev/null
+++ b/plugins-root/check_icmp.d/config.h
@@ -0,0 +1,115 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/states.h"
5#include <stddef.h>
6#include <netinet/in_systm.h>
7#include <netinet/in.h>
8#include <netinet/ip.h>
9#include <netinet/ip6.h>
10#include <netinet/ip_icmp.h>
11#include <netinet/icmp6.h>
12#include <arpa/inet.h>
13#include <stdint.h>
14#include "./check_icmp_helpers.h"
15#include "output.h"
16
17/* threshold structure. all values are maximum allowed, exclusive */
18typedef struct {
19 unsigned char pl; /* max allowed packet loss in percent */
20 time_t rta; /* roundtrip time average, microseconds */
21 double jitter; /* jitter time average, microseconds */
22 double mos; /* MOS */
23 double score; /* Score */
24} check_icmp_threshold;
25
26/* the different modes of this program are as follows:
27 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
28 * MODE_HOSTCHECK: Return immediately upon any sign of life
29 * In addition, sends packets to ALL addresses assigned
30 * to this host (as returned by gethostbyname() or
31 * gethostbyaddr() and expects one host only to be checked at
32 * a time. Therefore, any packet response what so ever will
33 * count as a sign of life, even when received outside
34 * crit.rta limit. Do not misspell any additional IP's.
35 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
36 * MODE_ICMP: Default Mode
37 */
38typedef enum {
39 MODE_RTA,
40 MODE_HOSTCHECK,
41 MODE_ALL,
42 MODE_ICMP,
43} check_icmp_execution_mode;
44
45typedef struct {
46 bool order_mode;
47 bool mos_mode;
48 bool rta_mode;
49 bool pl_mode;
50 bool jitter_mode;
51 bool score_mode;
52} check_icmp_mode_switches;
53
54typedef struct {
55 check_icmp_mode_switches modes;
56
57 int min_hosts_alive;
58 check_icmp_threshold crit;
59 check_icmp_threshold warn;
60
61 unsigned long ttl;
62 unsigned short icmp_data_size;
63 time_t target_interval;
64 unsigned short number_of_packets;
65
66 char *source_ip;
67 bool need_v4;
68 bool need_v6;
69
70 uint16_t sender_id; // PID of the main process, which is used as an ID in packets
71
72 check_icmp_execution_mode mode;
73
74 unsigned short number_of_targets;
75 ping_target *targets;
76
77 unsigned short number_of_hosts;
78 check_icmp_target_container *hosts;
79
80 mp_output_format output_format;
81 bool output_format_is_set;
82} check_icmp_config;
83
84check_icmp_config check_icmp_config_init();
85
86/* the data structure */
87typedef struct icmp_ping_data {
88 struct timeval stime; /* timestamp (saved in protocol struct as well) */
89 unsigned short ping_id;
90} icmp_ping_data;
91
92#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
93#define IP_HDR_SIZE 20
94#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
95#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
96#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
97
98/* 80 msec packet interval by default */
99// DEPRECATED, remove when removing the option
100#define DEFAULT_PKT_INTERVAL 80000
101
102#define DEFAULT_TARGET_INTERVAL 0
103
104#define DEFAULT_WARN_RTA 200000
105#define DEFAULT_CRIT_RTA 500000
106#define DEFAULT_WARN_PL 40
107#define DEFAULT_CRIT_PL 80
108
109#define DEFAULT_TIMEOUT 10
110#define DEFAULT_TTL 64
111
112#define DEFAULT_NUMBER_OF_PACKETS 5
113
114#define PACKET_BACKOFF_FACTOR 1.5
115#define TARGET_BACKOFF_FACTOR 1.5
diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t
index ce627736..70392154 100644
--- a/plugins-root/t/check_dhcp.t
+++ b/plugins-root/t/check_dhcp.t
@@ -12,14 +12,14 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 6; 15 plan tests => 7;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_dhcp"; 17 plan skip_all => "Need sudo to test check_dhcp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK: Received \d+ DHCPOFFER\(s\), \d+ of 1 requested servers responded, max lease time = \d+ sec\./'; 21my $successOutput = '/Received \d+ DHCPOFFER(s)*, max lease time = \d+ seconds/';
22my $failureOutput = '/CRITICAL: (No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; 22my $failureOutput = '/(No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/';
23my $invalidOutput = '/Invalid hostname/'; 23my $invalidOutput = '/Invalid hostname/';
24 24
25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE", 25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE",
@@ -34,6 +34,8 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
34 "An invalid (not known to DNS) hostname", 34 "An invalid (not known to DNS) hostname",
35 "nosuchhost" ); 35 "nosuchhost" );
36 36
37my $output_format = "--output-format mp-test-json";
38
37# try to determince interface 39# try to determince interface
38my $interface = ''; 40my $interface = '';
39 41
@@ -49,19 +51,21 @@ my $res;
49SKIP: { 51SKIP: {
50 skip('need responsive test host', 2) unless $host_responsive; 52 skip('need responsive test host', 2) unless $host_responsive;
51 $res = NPTest->testCmd( 53 $res = NPTest->testCmd(
52 "$sudo ./check_dhcp $interface -u -s $host_responsive" 54 "$sudo ./check_dhcp $interface -u -s $host_responsive $output_format"
53 ); 55 );
54 is( $res->return_code, 0, "Syntax ok" ); 56 is( $res->return_code, 0, "with JSON test format result should always be OK" );
55 like( $res->output, $successOutput, "Output OK" ); 57 like( $res->{'mp_test_result'}->{'state'}, "/OK/", "Output OK" );
58 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $successOutput, "Output OK" );
56}; 59};
57 60
58SKIP: { 61SKIP: {
59 skip('need nonresponsive test host', 2) unless $host_nonresponsive; 62 skip('need nonresponsive test host', 2) unless $host_nonresponsive;
60 $res = NPTest->testCmd( 63 $res = NPTest->testCmd(
61 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive" 64 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive $output_format"
62 ); 65 );
63 is( $res->return_code, 2, "Exit code - host nonresponsive" ); 66 is( $res->return_code, 0, "with JSON test format result should always be OK" );
64 like( $res->output, $failureOutput, "Output OK" ); 67 like( $res->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Exit code - host nonresponsive" );
68 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $failureOutput, "Output OK" );
65}; 69};
66 70
67SKIP: { 71SKIP: {
@@ -69,6 +73,6 @@ SKIP: {
69 $res = NPTest->testCmd( 73 $res = NPTest->testCmd(
70 "$sudo ./check_dhcp $interface -u -s $hostname_invalid" 74 "$sudo ./check_dhcp $interface -u -s $hostname_invalid"
71 ); 75 );
72 is( $res->return_code, 3, "Exit code - host invalid" ); 76 is( $res->return_code, 3, "invalid hostname/address should return UNKNOWN" );
73 like( $res->output, $invalidOutput, "Output OK" ); 77 like( $res->output, $invalidOutput, "Output OK" );
74}; 78};
diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t
index de1d88d2..d414c3c7 100644
--- a/plugins-root/t/check_icmp.t
+++ b/plugins-root/t/check_icmp.t
@@ -12,15 +12,12 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 40; 15 plan tests => 17;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_icmp"; 17 plan skip_all => "Need sudo to test check_icmp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK - .*? rta (?:[\d\.]+ms)|(?:nan), lost \d+%/';
22my $failureOutput = '/(WARNING|CRITICAL) - .*? rta (?:[\d\.]+ms > [\d\.]+ms|nan)/';
23
24my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE", 21my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE",
25 "The hostname of system responsive to network requests", 22 "The hostname of system responsive to network requests",
26 "localhost" ); 23 "localhost" );
@@ -36,108 +33,85 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
36my $res; 33my $res;
37 34
38$res = NPTest->testCmd( 35$res = NPTest->testCmd(
39 "$sudo ./check_icmp -H $host_responsive -w 10000ms,100% -c 10000ms,100%" 36 "$sudo ./check_icmp -H $host_responsive -w 100ms,100% -c 100ms,100%"
40 ); 37 );
41is( $res->return_code, 0, "Syntax ok" ); 38is( $res->return_code, 0, "Syntax ok" );
42like( $res->output, $successOutput, "Output OK" );
43 39
44$res = NPTest->testCmd( 40$res = NPTest->testCmd(
45 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 10000ms,100%" 41 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 100ms,100%"
46 ); 42 );
47is( $res->return_code, 1, "Syntax ok, with forced warning" ); 43is( $res->return_code, 1, "Syntax ok, with forced warning" );
48like( $res->output, $failureOutput, "Output OK" );
49 44
50$res = NPTest->testCmd( 45$res = NPTest->testCmd(
51 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%" 46 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%"
52 ); 47 );
53is( $res->return_code, 2, "Syntax ok, with forced critical" ); 48is( $res->return_code, 2, "Syntax ok, with forced critical" );
54like( $res->output, $failureOutput, "Output OK" );
55 49
56$res = NPTest->testCmd( 50$res = NPTest->testCmd(
57 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -t 2" 51 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100%"
58 ); 52 );
59is( $res->return_code, 2, "Timeout - host nonresponsive" ); 53is( $res->return_code, 2, "Timeout - host nonresponsive" );
60like( $res->output, '/pl=100%/', "Error contains 'pl=100%' string (for 100% packet loss)" );
61like( $res->output, '/rta=U/', "Error contains 'rta=U' string" );
62 54
63$res = NPTest->testCmd( 55$res = NPTest->testCmd(
64 "$sudo ./check_icmp -w 10000ms,100% -c 10000ms,100%" 56 "$sudo ./check_icmp -w 100ms,100% -c 100ms,100%"
65 ); 57 );
66is( $res->return_code, 3, "No hostname" ); 58is( $res->return_code, 3, "No hostname" );
67like( $res->output, '/No hosts to check/', "Output with appropriate error message");
68 59
69$res = NPTest->testCmd( 60$res = NPTest->testCmd(
70 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 0 -t 2" 61 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 0"
71 ); 62 );
72is( $res->return_code, 0, "One host nonresponsive - zero required" ); 63is( $res->return_code, 0, "One host nonresponsive - zero required" );
73like( $res->output, $successOutput, "Output OK" );
74 64
75$res = NPTest->testCmd( 65$res = NPTest->testCmd(
76 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 1 -t 2" 66 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 1"
77 ); 67 );
78is( $res->return_code, 0, "One of two host nonresponsive - one required" ); 68is( $res->return_code, 0, "One of two host nonresponsive - one required" );
79like( $res->output, $successOutput, "Output OK" );
80 69
81$res = NPTest->testCmd( 70$res = NPTest->testCmd(
82 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 71 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 2"
83 ); 72 );
84is( $res->return_code, 2, "One of two host nonresponsive - two required" ); 73is( $res->return_code, 2, "One of two host nonresponsive - two required" );
85like( $res->output, $failureOutput, "Output OK" );
86 74
87$res = NPTest->testCmd( 75$res = NPTest->testCmd(
88 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 76 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1"
89 ); 77 );
90is( $res->return_code, 0, "IPv4 source_ip accepted" ); 78is( $res->return_code, 0, "IPv4 source_ip accepted" );
91like( $res->output, $successOutput, "Output OK" );
92 79
93$res = NPTest->testCmd( 80$res = NPTest->testCmd(
94 "$sudo ./check_icmp -H $host_responsive -b 65507" 81 "$sudo ./check_icmp -H $host_responsive -b 65507"
95 ); 82 );
96is( $res->return_code, 0, "Try max packet size" ); 83is( $res->return_code, 0, "Try max packet size" );
97like( $res->output, $successOutput, "Output OK - Didn't overflow" );
98 84
99$res = NPTest->testCmd( 85$res = NPTest->testCmd(
100 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1 -t 2" 86 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1"
101 ); 87 );
102is( $res->return_code, 0, "rta works" ); 88is( $res->return_code, 0, "rta works" );
103like( $res->output, $successOutput, "Output OK" );
104$res = NPTest->testCmd( 89$res = NPTest->testCmd(
105 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1 -t 2" 90 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1"
106 ); 91 );
107is( $res->return_code, 0, "pl works" ); 92is( $res->return_code, 0, "pl works" );
108like( $res->output, '/lost 0%/', "Output OK" );
109 93
110$res = NPTest->testCmd( 94$res = NPTest->testCmd(
111 "$sudo ./check_icmp -H $host_responsive -J 80,90 -t 2" 95 "$sudo ./check_icmp -H $host_responsive -J 80,90"
112 ); 96 );
113is( $res->return_code, 0, "jitter works" ); 97is( $res->return_code, 0, "jitter works" );
114like( $res->output, '/jitter \d/', "Output OK" );
115 98
116$res = NPTest->testCmd( 99$res = NPTest->testCmd(
117 "$sudo ./check_icmp -H $host_responsive -M 4,3 -t 2" 100 "$sudo ./check_icmp -H $host_responsive -M 4,3"
118 ); 101 );
119is( $res->return_code, 0, "mos works" ); 102is( $res->return_code, 0, "mos works" );
120like( $res->output, '/MOS \d/', "Output OK" );
121 103
122$res = NPTest->testCmd( 104$res = NPTest->testCmd(
123 "$sudo ./check_icmp -H $host_responsive -S 80,70 -t 2" 105 "$sudo ./check_icmp -H $host_responsive -S 80,70"
124 ); 106 );
125is( $res->return_code, 0, "score works" ); 107is( $res->return_code, 0, "score works" );
126like( $res->output, '/Score \d/', "Output OK" );
127 108
128$res = NPTest->testCmd( 109$res = NPTest->testCmd(
129 "$sudo ./check_icmp -H $host_responsive -O -t 2" 110 "$sudo ./check_icmp -H $host_responsive -O"
130 ); 111 );
131is( $res->return_code, 0, "order works" ); 112is( $res->return_code, 0, "order works" );
132like( $res->output, '/Packets in order/', "Output OK" );
133 113
134$res = NPTest->testCmd( 114$res = NPTest->testCmd(
135 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100 -t 2" 115 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100"
136 ); 116 );
137is( $res->return_code, 0, "order works" ); 117is( $res->return_code, 0, "order works" );
138like( $res->output, '/Packets in order/', "Output OK" );
139like( $res->output, '/Score \d/', "Output OK" );
140like( $res->output, '/MOS \d/', "Output OK" );
141like( $res->output, '/jitter \d/', "Output OK" );
142like( $res->output, '/lost 0%/', "Output OK" );
143like( $res->output, $successOutput, "Output OK" );
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 04fb7ed2..deae938d 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
@@ -30,22 +36,25 @@ libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http che
30 check_mrtg check_mrtgtraf check_ntp check_ntp_peer 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 \
44 tests/test_check_disk 52 tests/test_check_disk
45 53
46SUBDIRS = picohttpparser 54SUBDIRS = picohttpparser
47 55
48np_test_scripts = tests/test_check_swap.t \ 56np_test_scripts = tests/test_check_swap.t \
57 tests/test_check_snmp.t \
49 tests/test_check_disk.t 58 tests/test_check_disk.t
50 59
51EXTRA_DIST = t \ 60EXTRA_DIST = t \
@@ -59,6 +68,8 @@ EXTRA_DIST = t \
59 check_radius.d \ 68 check_radius.d \
60 check_disk.d \ 69 check_disk.d \
61 check_time.d \ 70 check_time.d \
71 check_users.d \
72 check_load.d \
62 check_nagios.d \ 73 check_nagios.d \
63 check_dbi.d \ 74 check_dbi.d \
64 check_tcp.d \ 75 check_tcp.d \
@@ -72,9 +83,11 @@ EXTRA_DIST = t \
72 check_ntp_peer.d \ 83 check_ntp_peer.d \
73 check_apt.d \ 84 check_apt.d \
74 check_pgsql.d \ 85 check_pgsql.d \
86 check_procs.d \
75 check_ping.d \ 87 check_ping.d \
76 check_by_ssh.d \ 88 check_by_ssh.d \
77 check_smtp.d \ 89 check_smtp.d \
90 check_snmp.d \
78 check_mysql.d \ 91 check_mysql.d \
79 check_ntp_time.d \ 92 check_ntp_time.d \
80 check_dig.d \ 93 check_dig.d \
@@ -148,7 +161,10 @@ check_ping_LDADD = $(NETLIBS)
148check_procs_LDADD = $(BASEOBJS) 161check_procs_LDADD = $(BASEOBJS)
149check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) 162check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
150check_real_LDADD = $(NETLIBS) 163check_real_LDADD = $(NETLIBS)
164check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c
151check_snmp_LDADD = $(BASEOBJS) 165check_snmp_LDADD = $(BASEOBJS)
166check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs`
167check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags`
152check_smtp_LDADD = $(SSLOBJS) 168check_smtp_LDADD = $(SSLOBJS)
153check_ssh_LDADD = $(NETLIBS) 169check_ssh_LDADD = $(NETLIBS)
154check_swap_SOURCES = check_swap.c check_swap.d/swap.c 170check_swap_SOURCES = check_swap.c check_swap.d/swap.c
@@ -157,6 +173,7 @@ check_tcp_LDADD = $(SSLOBJS)
157check_time_LDADD = $(NETLIBS) 173check_time_LDADD = $(NETLIBS)
158check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) 174check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
159check_ups_LDADD = $(NETLIBS) 175check_ups_LDADD = $(NETLIBS)
176check_users_SOURCES = check_users.c check_users.d/users.c
160check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) 177check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
161check_by_ssh_LDADD = $(NETLIBS) 178check_by_ssh_LDADD = $(NETLIBS)
162check_ide_smart_LDADD = $(BASEOBJS) 179check_ide_smart_LDADD = $(BASEOBJS)
@@ -169,6 +186,8 @@ endif
169 186
170tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap 187tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
171tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c 188tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c
189tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap
190tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c
172tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap 191tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap
173tests_test_check_disk_SOURCES = tests/test_check_disk.c 192tests_test_check_disk_SOURCES = tests/test_check_disk.c
174 193
diff --git a/plugins/check_fping.c b/plugins/check_fping.c
index ec7abb67..8018e06d 100644
--- a/plugins/check_fping.c
+++ b/plugins/check_fping.c
@@ -79,6 +79,24 @@ int main(int argc, char **argv) {
79 server = strscpy(server, config.server_name); 79 server = strscpy(server, config.server_name);
80 80
81 char *option_string = ""; 81 char *option_string = "";
82 char *fping_prog = NULL;
83
84 /* First determine if the target is dualstack or ipv6 only. */
85 bool server_is_inet6_addr = is_inet6_addr(server);
86
87 /*
88 * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack
89 * -> we use ipv6
90 * If the user requested -4 OR the user made no assertion and the address is v4 ONLY
91 * -> we use ipv4
92 */
93 if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) {
94 xasprintf(&option_string, "%s-6 ", option_string);
95 } else {
96 xasprintf(&option_string, "%s-4 ", option_string);
97 }
98 fping_prog = strdup(PATH_TO_FPING);
99
82 /* compose the command */ 100 /* compose the command */
83 if (config.target_timeout) { 101 if (config.target_timeout) {
84 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout); 102 xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout);
@@ -99,19 +117,26 @@ int main(int argc, char **argv) {
99 xasprintf(&option_string, "%s-R ", option_string); 117 xasprintf(&option_string, "%s-R ", option_string);
100 } 118 }
101 119
102 char *fping_prog = NULL; 120 if (config.fwmark_set) {
103#ifdef PATH_TO_FPING6 121 xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark);
104 if (address_family != AF_INET && is_inet6_addr(server)) { 122 }
105 fping_prog = strdup(PATH_TO_FPING6); 123
106 } else { 124 if (config.icmp_timestamp) {
107 fping_prog = strdup(PATH_TO_FPING); 125 xasprintf(&option_string, "%s--icmp-timestamp ", option_string);
126 }
127
128 if (config.check_source) {
129 xasprintf(&option_string, "%s--check-source ", option_string);
108 } 130 }
109#else
110 fping_prog = strdup(PATH_TO_FPING);
111#endif
112 131
113 char *command_line = NULL; 132 char *command_line = NULL;
114 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); 133
134 if (config.icmp_timestamp) {
135 // no packet size settable for ICMP timestamp
136 xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, server);
137 } else {
138 xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server);
139 }
115 140
116 if (verbose) { 141 if (verbose) {
117 printf("%s\n", command_line); 142 printf("%s\n", command_line);
@@ -268,13 +293,38 @@ mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double c
268 293
269/* process command-line arguments */ 294/* process command-line arguments */
270check_fping_config_wrapper process_arguments(int argc, char **argv) { 295check_fping_config_wrapper process_arguments(int argc, char **argv) {
271 static struct option longopts[] = { 296 enum {
272 {"hostname", required_argument, 0, 'H'}, {"sourceip", required_argument, 0, 'S'}, {"sourceif", required_argument, 0, 'I'}, 297 FWMARK_OPT = CHAR_MAX + 1,
273 {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, {"alive", no_argument, 0, 'a'}, 298 ICMP_TIMESTAMP_OPT,
274 {"bytes", required_argument, 0, 'b'}, {"number", required_argument, 0, 'n'}, {"target-timeout", required_argument, 0, 'T'}, 299 CHECK_SOURCE_OPT,
275 {"interval", required_argument, 0, 'i'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, 300 };
276 {"help", no_argument, 0, 'h'}, {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, 301 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
277 {"dontfrag", no_argument, 0, 'M'}, {"random", no_argument, 0, 'R'}, {0, 0, 0, 0}}; 302 {"sourceip", required_argument, 0, 'S'},
303 {"sourceif", required_argument, 0, 'I'},
304 {"critical", required_argument, 0, 'c'},
305 {"warning", required_argument, 0, 'w'},
306 {"alive", no_argument, 0, 'a'},
307 {"bytes", required_argument, 0, 'b'},
308 {"number", required_argument, 0, 'n'},
309 {"target-timeout", required_argument, 0, 'T'},
310 {"interval", required_argument, 0, 'i'},
311 {"verbose", no_argument, 0, 'v'},
312 {"version", no_argument, 0, 'V'},
313 {"help", no_argument, 0, 'h'},
314 {"use-ipv4", no_argument, 0, '4'},
315 {"use-ipv6", no_argument, 0, '6'},
316 {"dontfrag", no_argument, 0, 'M'},
317 {"random", no_argument, 0, 'R'},
318#ifdef FPING_VERSION_5_2_OR_HIGHER
319 // only available with fping version >= 5.2
320 {"fwmark", required_argument, NULL, FWMARK_OPT},
321# ifdef FPING_VERSION_5_3_OR_HIGHER
322 // only available with fping version >= 5.3
323 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT},
324 {"check-source", no_argument, NULL, CHECK_SOURCE_OPT},
325# endif
326#endif
327 {0, 0, 0, 0}};
278 328
279 char *rv[2]; 329 char *rv[2];
280 rv[PL] = NULL; 330 rv[PL] = NULL;
@@ -299,7 +349,7 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) {
299 argc--; 349 argc--;
300 } 350 }
301 351
302 while (1) { 352 while (true) {
303 int option_index = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); 353 int option_index = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option);
304 354
305 if (option_index == -1 || option_index == EOF || option_index == 1) { 355 if (option_index == -1 || option_index == EOF || option_index == 1) {
@@ -340,11 +390,7 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) {
340 address_family = AF_INET; 390 address_family = AF_INET;
341 break; 391 break;
342 case '6': /* IPv6 only */ 392 case '6': /* IPv6 only */
343#ifdef USE_IPV6
344 address_family = AF_INET6; 393 address_family = AF_INET6;
345#else
346 usage(_("IPv6 support not available\n"));
347#endif
348 break; 394 break;
349 case 'c': 395 case 'c':
350 get_threshold(optarg, rv); 396 get_threshold(optarg, rv);
@@ -406,6 +452,20 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) {
406 case 'M': 452 case 'M':
407 result.config.dontfrag = true; 453 result.config.dontfrag = true;
408 break; 454 break;
455 case FWMARK_OPT:
456 if (is_intpos(optarg)) {
457 result.config.fwmark = (unsigned int)atol(optarg);
458 result.config.fwmark_set = true;
459 } else {
460 usage(_("fwmark must be a positive integer"));
461 }
462 break;
463 case ICMP_TIMESTAMP_OPT:
464 result.config.icmp_timestamp = true;
465 break;
466 case CHECK_SOURCE_OPT:
467 result.config.check_source = true;
468 break;
409 } 469 }
410 } 470 }
411 471
@@ -493,6 +553,16 @@ void print_help(void) {
493 printf(" %s\n", _("set the Don't Fragment flag")); 553 printf(" %s\n", _("set the Don't Fragment flag"));
494 printf(" %s\n", "-R, --random"); 554 printf(" %s\n", "-R, --random");
495 printf(" %s\n", _("random packet data (to foil link data compression)")); 555 printf(" %s\n", _("random packet data (to foil link data compression)"));
556#ifdef FPING_VERSION_5_2_OR_HIGHER
557 printf(" %s\n", "--fwmark=INTEGER");
558 printf(" %s\n", _("set the routing mark to INTEGER (fping option)"));
559# ifdef FPING_VERSION_5_3_OR_HIGHER
560 printf(" %s\n", "--icmp-timestamp");
561 printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)"));
562 printf(" %s\n", "--check-source");
563 printf(" %s\n", _("discard replies not from target address (fping option)"));
564# endif
565#endif
496 printf(UT_VERBOSE); 566 printf(UT_VERBOSE);
497 printf("\n"); 567 printf("\n");
498 printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)")); 568 printf(" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h
index a0697bf3..d95e9ded 100644
--- a/plugins/check_fping.d/config.h
+++ b/plugins/check_fping.d/config.h
@@ -29,6 +29,21 @@ typedef struct {
29 bool cpl_p; 29 bool cpl_p;
30 int wpl; 30 int wpl;
31 bool wpl_p; 31 bool wpl_p;
32
33 // only available with fping version >= 5.2
34 // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark
35 // in the packets
36 unsigned int fwmark;
37 bool fwmark_set;
38
39
40 // only available with fping version >= 5.3
41 // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead
42 // of ICMP Echo
43 bool icmp_timestamp;
44
45 // Setting check_source lets fping discard replies which are not from the target address
46 bool check_source;
32} check_fping_config; 47} check_fping_config;
33 48
34check_fping_config check_fping_config_init() { 49check_fping_config check_fping_config_init() {
@@ -53,6 +68,15 @@ check_fping_config check_fping_config_init() {
53 .cpl_p = false, 68 .cpl_p = false,
54 .wpl = 0, 69 .wpl = 0,
55 .wpl_p = false, 70 .wpl_p = false,
71
72 // only available with fping version >= 5.2
73 .fwmark = 0,
74 .fwmark_set = false, // just to be deterministic
75
76 // only available with fping version >= 5.3
77 .icmp_timestamp = false,
78 .check_source = false,
79
56 }; 80 };
57 return tmp; 81 return tmp;
58} 82}
diff --git a/plugins/check_http.c b/plugins/check_http.c
index baff682a..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_load.c b/plugins/check_load.c
index 1431d130..2925bff3 100644
--- a/plugins/check_load.c
+++ b/plugins/check_load.c
@@ -1,350 +1,423 @@
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 } 131 }
136 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); 132
137 if (child_stderr == NULL) { 133 const check_load_config config = tmp_config.config;
138 printf (_("Could not open stderr for %s\n"), PATH_TO_UPTIME); 134
135 double load_values[3] = {0, 0, 0};
136
137 // this should be getloadavg from gnulib, should work everywhereâ„¢
138 int error = getloadavg(load_values, 3);
139 if (error != 3) {
140 die(STATE_UNKNOWN, _("Failed to retrieve load values"));
139 } 141 }
140 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process); 142
141 if(strstr(input_buffer, "load average:")) { 143 mp_check overall = mp_check_init();
142 sscanf (input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15); 144 if (config.output_format_set) {
143 } 145 mp_set_format(config.output_format);
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 } 146 }
157#endif 147
158 148 bool is_using_scaled_load_values = false;
159 if ((la[0] < 0.0) || (la[1] < 0.0) || (la[2] < 0.0)) { 149 long numcpus;
160#ifdef HAVE_GETLOADAVG 150 if (config.take_into_account_cpus && ((numcpus = GET_NUMBER_OF_CPUS()) > 0)) {
161 printf (_("Error in getloadavg()\n")); 151 is_using_scaled_load_values = true;
162#else 152
163 printf (_("Error processing %s\n"), PATH_TO_UPTIME); 153 double scaled_la[3] = {
164#endif 154 load_values[0] / numcpus,
165 return STATE_UNKNOWN; 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", pd_value_to_string(pd_scaled_load1.value));
172 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc1);
173
174 mp_perfdata pd_scaled_load5 = perfdata_init();
175 pd_scaled_load5.label = "scaled_load5";
176 pd_scaled_load5 = mp_set_pd_value(pd_scaled_load5, scaled_la[1]);
177 pd_scaled_load5 = mp_pd_set_thresholds(pd_scaled_load5, config.th_load[1]);
178
179 mp_subcheck scaled_load_sc5 = mp_subcheck_init();
180 scaled_load_sc5 = mp_set_subcheck_state(scaled_load_sc5, mp_get_pd_status(pd_scaled_load5));
181 mp_add_perfdata_to_subcheck(&scaled_load_sc5, pd_scaled_load5);
182 xasprintf(&scaled_load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_scaled_load5.value));
183 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc5);
184
185 mp_perfdata pd_scaled_load15 = perfdata_init();
186 pd_scaled_load15.label = "scaled_load15";
187 pd_scaled_load15 = mp_set_pd_value(pd_scaled_load15, scaled_la[2]);
188 pd_scaled_load15 = mp_pd_set_thresholds(pd_scaled_load15, config.th_load[2]);
189
190 mp_subcheck scaled_load_sc15 = mp_subcheck_init();
191 scaled_load_sc15 = mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15));
192 mp_add_perfdata_to_subcheck(&scaled_load_sc15, pd_scaled_load15);
193 xasprintf(&scaled_load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_scaled_load15.value));
194 mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc15);
195
196 mp_add_subcheck_to_check(&overall, scaled_load_sc);
166 } 197 }
167 198
168 /* we got this far, so assume OK until we've measured */ 199 mp_subcheck load_sc = mp_subcheck_init();
169 result = STATE_OK; 200 load_sc = mp_set_subcheck_default_state(load_sc, STATE_OK);
201 load_sc.output = "Total Load";
170 202
171 xasprintf(&status_line, _("load average: %.2f, %.2f, %.2f"), la1, la5, la15); 203 mp_perfdata pd_load1 = perfdata_init();
172 xasprintf(&status_line, ("total %s"), status_line); 204 pd_load1.label = "load1";
205 pd_load1 = mp_set_pd_value(pd_load1, load_values[0]);
206 if (!is_using_scaled_load_values) {
207 pd_load1 = mp_pd_set_thresholds(pd_load1, config.th_load[0]);
208 }
173 209
210 mp_subcheck load_sc1 = mp_subcheck_init();
211 load_sc1 = mp_set_subcheck_state(load_sc1, mp_get_pd_status(pd_load1));
212 mp_add_perfdata_to_subcheck(&load_sc1, pd_load1);
213 xasprintf(&load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_load1.value));
214 mp_add_subcheck_to_subcheck(&load_sc, load_sc1);
215
216 mp_perfdata pd_load5 = perfdata_init();
217 pd_load5.label = "load5";
218 pd_load5 = mp_set_pd_value(pd_load5, load_values[1]);
219 if (!is_using_scaled_load_values) {
220 pd_load5 = mp_pd_set_thresholds(pd_load5, config.th_load[1]);
221 }
174 222
175 double scaled_la[3] = { 0.0, 0.0, 0.0 }; 223 mp_subcheck load_sc5 = mp_subcheck_init();
176 bool is_using_scaled_load_values = false; 224 load_sc5 = mp_set_subcheck_state(load_sc5, mp_get_pd_status(pd_load5));
225 mp_add_perfdata_to_subcheck(&load_sc5, pd_load5);
226 xasprintf(&load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_load5.value));
227 mp_add_subcheck_to_subcheck(&load_sc, load_sc5);
228
229 mp_perfdata pd_load15 = perfdata_init();
230 pd_load15.label = "load15";
231 pd_load15 = mp_set_pd_value(pd_load15, load_values[2]);
232 if (!is_using_scaled_load_values) {
233 pd_load15 = mp_pd_set_thresholds(pd_load15, config.th_load[2]);
234 }
177 235
178 if (take_into_account_cpus == true && (numcpus = GET_NUMBER_OF_CPUS()) > 0) { 236 mp_subcheck load_sc15 = mp_subcheck_init();
179 is_using_scaled_load_values = true; 237 load_sc15 = mp_set_subcheck_state(load_sc15, mp_get_pd_status(pd_load15));
238 mp_add_perfdata_to_subcheck(&load_sc15, pd_load15);
239 xasprintf(&load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_load15.value));
240 mp_add_subcheck_to_subcheck(&load_sc, load_sc15);
180 241
181 scaled_la[0] = la[0] / numcpus; 242 mp_add_subcheck_to_check(&overall, load_sc);
182 scaled_la[1] = la[1] / numcpus;
183 scaled_la[2] = la[2] / numcpus;
184 243
185 char *tmp = NULL; 244 if (config.n_procs_to_show > 0) {
186 xasprintf(&tmp, _("load average: %.2f, %.2f, %.2f"), scaled_la[0], scaled_la[1], scaled_la[2]); 245 mp_subcheck top_proc_sc = mp_subcheck_init();
187 xasprintf(&status_line, "scaled %s - %s", tmp, status_line); 246 top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK);
188 } 247 top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show);
248 xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes", config.n_procs_to_show);
189 249
190 for(i = 0; i < 3; i++) { 250 if (top_proc.errorcode == OK) {
191 if (is_using_scaled_load_values) { 251 for (unsigned long i = 0; i < config.n_procs_to_show; i++) {
192 if(scaled_la[i] > cload[i]) { 252 xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, top_proc.top_processes[i]);
193 result = STATE_CRITICAL;
194 break;
195 }
196 else if(scaled_la[i] > wload[i]) result = STATE_WARNING;
197 } else {
198 if(la[i] > cload[i]) {
199 result = STATE_CRITICAL;
200 break;
201 } 253 }
202 else if(la[i] > wload[i]) result = STATE_WARNING;
203 } 254 }
204 }
205 255
206 printf("LOAD %s - %s|", state_text(result), status_line); 256 mp_add_subcheck_to_check(&overall, top_proc_sc);
207 for(i = 0; i < 3; i++) {
208 if (is_using_scaled_load_values) {
209 printf("load%d=%.3f;;;0; ", nums[i], la[i]);
210 printf("scaled_load%d=%.3f;%.3f;%.3f;0; ", nums[i], scaled_la[i], wload[i], cload[i]);
211 } else {
212 printf("load%d=%.3f;%.3f;%.3f;0; ", nums[i], la[i], wload[i], cload[i]);
213 }
214 } 257 }
215 258
216 putchar('\n'); 259 mp_exit(overall);
217 if (n_procs_to_show > 0) {
218 print_top_consuming_processes();
219 }
220 return result;
221} 260}
222 261
223
224/* process command-line arguments */ 262/* process command-line arguments */
225static int 263static check_load_config_wrapper process_arguments(int argc, char **argv) {
226process_arguments (int argc, char **argv) 264
227{ 265 enum {
228 int c = 0; 266 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 }; 267 };
240 268
241 if (argc < 2) 269 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
242 return ERROR; 270 {"critical", required_argument, 0, 'c'},
271 {"percpu", no_argument, 0, 'r'},
272 {"version", no_argument, 0, 'V'},
273 {"help", no_argument, 0, 'h'},
274 {"procs-to-show", required_argument, 0, 'n'},
275 {"output-format", required_argument, 0, output_format_index},
276 {0, 0, 0, 0}};
277
278 check_load_config_wrapper result = {
279 .errorcode = OK,
280 .config = check_load_config_init(),
281 };
243 282
244 while (1) { 283 if (argc < 2) {
245 c = getopt_long (argc, argv, "Vhrc:w:n:", longopts, &option); 284 result.errorcode = ERROR;
285 return result;
286 }
246 287
247 if (c == -1 || c == EOF) 288 while (true) {
248 break; 289 int option = 0;
290 int option_index = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option);
249 291
250 switch (c) { 292 if (option_index == -1 || option_index == EOF) {
251 case 'w': /* warning time threshold */
252 get_threshold(optarg, wload);
253 break; 293 break;
254 case 'c': /* critical time threshold */ 294 }
255 get_threshold(optarg, cload); 295
296 switch (option_index) {
297 case output_format_index: {
298 parsed_output_format parser = mp_parse_output_format(optarg);
299 if (!parser.parsing_success) {
300 printf("Invalid output format: %s\n", optarg);
301 exit(STATE_UNKNOWN);
302 }
303
304 result.config.output_format_set = true;
305 result.config.output_format = parser.output_format;
256 break; 306 break;
307 }
308 case 'w': /* warning time threshold */ {
309 parsed_thresholds warning_range = get_threshold(optarg);
310 result.config.th_load[0].warning = warning_range.load[0];
311 result.config.th_load[0].warning_is_set = true;
312
313 result.config.th_load[1].warning = warning_range.load[1];
314 result.config.th_load[1].warning_is_set = true;
315
316 result.config.th_load[2].warning = warning_range.load[2];
317 result.config.th_load[2].warning_is_set = true;
318 } break;
319 case 'c': /* critical time threshold */ {
320 parsed_thresholds critical_range = get_threshold(optarg);
321 result.config.th_load[0].critical = critical_range.load[0];
322 result.config.th_load[0].critical_is_set = true;
323
324 result.config.th_load[1].critical = critical_range.load[1];
325 result.config.th_load[1].critical_is_set = true;
326
327 result.config.th_load[2].critical = critical_range.load[2];
328 result.config.th_load[2].critical_is_set = true;
329 } break;
257 case 'r': /* Divide load average by number of CPUs */ 330 case 'r': /* Divide load average by number of CPUs */
258 take_into_account_cpus = true; 331 result.config.take_into_account_cpus = true;
259 break; 332 break;
260 case 'V': /* version */ 333 case 'V': /* version */
261 print_revision (progname, NP_VERSION); 334 print_revision(progname, NP_VERSION);
262 exit (STATE_UNKNOWN); 335 exit(STATE_UNKNOWN);
263 case 'h': /* help */ 336 case 'h': /* help */
264 print_help (); 337 print_help();
265 exit (STATE_UNKNOWN); 338 exit(STATE_UNKNOWN);
266 case 'n': 339 case 'n':
267 n_procs_to_show = atoi(optarg); 340 result.config.n_procs_to_show = (unsigned long)atol(optarg);
268 break; 341 break;
269 case '?': /* help */ 342 case '?': /* help */
270 usage5 (); 343 usage5();
271 } 344 }
272 } 345 }
273 346
274 c = optind; 347 int index = optind;
275 if (c == argc) 348 if (index == argc) {
276 return validate_arguments (); 349 return result;
350 }
277 351
278 /* handle the case if both arguments are missing, 352 /* handle the case if both arguments are missing,
279 * but not if only one is given without -c or -w flag */ 353 * but not if only one is given without -c or -w flag */
280 if(c - argc == 2) { 354 if (index - argc == 2) {
281 get_threshold(argv[c++], wload); 355 parsed_thresholds warning_range = get_threshold(argv[index++]);
282 get_threshold(argv[c++], cload); 356 result.config.th_load[0].warning = warning_range.load[0];
283 } 357 result.config.th_load[0].warning_is_set = true;
284 else if(c - argc == 1) { 358
285 get_threshold(argv[c++], cload); 359 result.config.th_load[1].warning = warning_range.load[1];
360 result.config.th_load[1].warning_is_set = true;
361
362 result.config.th_load[2].warning = warning_range.load[2];
363 result.config.th_load[2].warning_is_set = true;
364 parsed_thresholds critical_range = get_threshold(argv[index++]);
365 result.config.th_load[0].critical = critical_range.load[0];
366 result.config.th_load[0].critical_is_set = true;
367
368 result.config.th_load[1].critical = critical_range.load[1];
369 result.config.th_load[1].critical_is_set = true;
370
371 result.config.th_load[2].critical = critical_range.load[2];
372 result.config.th_load[2].critical_is_set = true;
373 } else if (index - argc == 1) {
374 parsed_thresholds critical_range = get_threshold(argv[index++]);
375 result.config.th_load[0].critical = critical_range.load[0];
376 result.config.th_load[0].critical_is_set = true;
377
378 result.config.th_load[1].critical = critical_range.load[1];
379 result.config.th_load[1].critical_is_set = true;
380
381 result.config.th_load[2].critical = critical_range.load[2];
382 result.config.th_load[2].critical_is_set = true;
286 } 383 }
287 384
288 return validate_arguments (); 385 return result;
289}
290
291
292static int
293validate_arguments (void)
294{
295 int i = 0;
296
297 /* match cload first, as it will give the most friendly error message
298 * if user hasn't given the -c switch properly */
299 for(i = 0; i < 3; i++) {
300 if(cload[i] < 0)
301 die (STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]);
302 if(wload[i] < 0)
303 die (STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]);
304 if(wload[i] > cload[i])
305 die (STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]);
306 }
307
308 return OK;
309} 386}
310 387
388void print_help(void) {
389 print_revision(progname, NP_VERSION);
311 390
312void 391 printf("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n");
313print_help (void) 392 printf(COPYRIGHT, copyright, email);
314{
315 print_revision (progname, NP_VERSION);
316
317 printf ("Copyright (c) 1999 Felipe Gustavo de Almeida <galmeida@linux.ime.usp.br>\n");
318 printf (COPYRIGHT, copyright, email);
319 393
320 printf (_("This plugin tests the current system load average.")); 394 printf(_("This plugin tests the current system load average."));
321 395
322 printf ("\n\n"); 396 printf("\n\n");
323 397
324 print_usage (); 398 print_usage();
325 399
326 printf (UT_HELP_VRSN); 400 printf(UT_HELP_VRSN);
327 printf (UT_EXTRA_OPTS); 401 printf(UT_EXTRA_OPTS);
328 402
329 printf (" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); 403 printf(" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15");
330 printf (" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); 404 printf(" %s\n", _("Exit with WARNING status if load average exceeds WLOADn"));
331 printf (" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15"); 405 printf(" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15");
332 printf (" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn")); 406 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\"")); 407 printf(" %s\n", _("the load average format is the same used by \"uptime\" and \"w\""));
334 printf (" %s\n", "-r, --percpu"); 408 printf(" %s\n", "-r, --percpu");
335 printf (" %s\n", _("Divide the load averages by the number of CPUs (when possible)")); 409 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"); 410 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.")); 411 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")); 412 printf(" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0"));
339 413
340 printf (UT_SUPPORT); 414 printf(UT_OUTPUT_FORMAT);
415 printf(UT_SUPPORT);
341} 416}
342 417
343void 418void print_usage(void) {
344print_usage (void) 419 printf("%s\n", _("Usage:"));
345{ 420 printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname);
346 printf ("%s\n", _("Usage:"));
347 printf ("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname);
348} 421}
349 422
350#ifdef PS_USES_PROCPCPU 423#ifdef PS_USES_PROCPCPU
@@ -356,36 +429,51 @@ int cmpstringp(const void *p1, const void *p2) {
356 int procrss = 0; 429 int procrss = 0;
357 float procpcpu = 0; 430 float procpcpu = 0;
358 char procstat[8]; 431 char procstat[8];
359#ifdef PS_USES_PROCETIME 432# ifdef PS_USES_PROCETIME
360 char procetime[MAX_INPUT_BUFFER]; 433 char procetime[MAX_INPUT_BUFFER];
361#endif /* PS_USES_PROCETIME */ 434# endif /* PS_USES_PROCETIME */
362 char procprog[MAX_INPUT_BUFFER]; 435 char procprog[MAX_INPUT_BUFFER];
363 int pos; 436 int pos;
364 sscanf (* (char * const *) p1, PS_FORMAT, PS_VARLIST); 437 sscanf(*(char *const *)p1, PS_FORMAT, PS_VARLIST);
365 float procpcpu1 = procpcpu; 438 float procpcpu1 = procpcpu;
366 sscanf (* (char * const *) p2, PS_FORMAT, PS_VARLIST); 439 sscanf(*(char *const *)p2, PS_FORMAT, PS_VARLIST);
367 return procpcpu1 < procpcpu; 440 return procpcpu1 < procpcpu;
368} 441}
369#endif /* PS_USES_PROCPCPU */ 442#endif /* PS_USES_PROCPCPU */
370 443
371static int print_top_consuming_processes() { 444static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show) {
372 int i = 0; 445 top_processes_result result = {
373 struct output chld_out, chld_err; 446 .errorcode = OK,
374 if(np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0){ 447 };
448 struct output chld_out;
449 struct output chld_err;
450 if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) {
375 fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); 451 fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND);
376 return STATE_UNKNOWN; 452 result.errorcode = ERROR;
453 return result;
377 } 454 }
455
378 if (chld_out.lines < 2) { 456 if (chld_out.lines < 2) {
379 fprintf(stderr, _("some error occurred getting procs list.\n")); 457 fprintf(stderr, _("some error occurred getting procs list.\n"));
380 return STATE_UNKNOWN; 458 result.errorcode = ERROR;
459 return result;
381 } 460 }
461
382#ifdef PS_USES_PROCPCPU 462#ifdef PS_USES_PROCPCPU
383 qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char*), cmpstringp); 463 qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp);
384#endif /* PS_USES_PROCPCPU */ 464#endif /* PS_USES_PROCPCPU */
385 int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) 465 unsigned long lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1;
386 ? (int)chld_out.lines : n_procs_to_show + 1; 466
387 for (i = 0; i < lines_to_show; i += 1) { 467 result.top_processes = calloc(lines_to_show, sizeof(char *));
388 printf("%s\n", chld_out.line[i]); 468 if (result.top_processes == NULL) {
469 // Failed allocation
470 result.errorcode = ERROR;
471 return result;
389 } 472 }
390 return OK; 473
474 for (unsigned long i = 0; i < lines_to_show; i += 1) {
475 xasprintf(&result.top_processes[i], "%s", chld_out.line[i]);
476 }
477
478 return result;
391} 479}
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_ntp_peer.c b/plugins/check_ntp_peer.c
index 6e76bf23..5c4ff386 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -707,11 +707,11 @@ int main(int argc, char *argv[]) {
707 707
708 if (config.do_stratum) { 708 if (config.do_stratum) {
709 if (sresult == STATE_WARNING) { 709 if (sresult == STATE_WARNING) {
710 xasprintf(&result_line, "%s, stratum=%l (WARNING)", result_line, ntp_res.stratum); 710 xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum);
711 } else if (sresult == STATE_CRITICAL) { 711 } else if (sresult == STATE_CRITICAL) {
712 xasprintf(&result_line, "%s, stratum=%l (CRITICAL)", result_line, ntp_res.stratum); 712 xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum);
713 } else { 713 } else {
714 xasprintf(&result_line, "%s, stratum=%l", result_line, ntp_res.stratum); 714 xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum);
715 } 715 }
716 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds)); 716 xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds));
717 } 717 }
diff --git a/plugins/check_procs.c b/plugins/check_procs.c
index 1d78ccee..83e6864e 100644
--- a/plugins/check_procs.c
+++ b/plugins/check_procs.c
@@ -1,41 +1,41 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_procs plugin 3 * Monitoring check_procs plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_procs plugin 10 * This file contains the check_procs plugin
11* 11 *
12* Checks all processes and generates WARNING or CRITICAL states if the 12 * Checks all processes and generates WARNING or CRITICAL states if the
13* specified metric is outside the required threshold ranges. The metric 13 * specified metric is outside the required threshold ranges. The metric
14* defaults to number of processes. Search filters can be applied to limit 14 * defaults to number of processes. Search filters can be applied to limit
15* the processes to check. 15 * the processes to check.
16* 16 *
17* The parent process, check_procs itself and any child process of 17 * The parent process, check_procs itself and any child process of
18* check_procs (ps) are excluded from any checks to prevent false positives. 18 * check_procs (ps) are excluded from any checks to prevent false positives.
19* 19 *
20* 20 *
21* This program is free software: you can redistribute it and/or modify 21 * This program is free software: you can redistribute it and/or modify
22* it under the terms of the GNU General Public License as published by 22 * it under the terms of the GNU General Public License as published by
23* the Free Software Foundation, either version 3 of the License, or 23 * the Free Software Foundation, either version 3 of the License, or
24* (at your option) any later version. 24 * (at your option) any later version.
25* 25 *
26* This program is distributed in the hope that it will be useful, 26 * This program is distributed in the hope that it will be useful,
27* but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29* GNU General Public License for more details. 29 * GNU General Public License for more details.
30* 30 *
31* You should have received a copy of the GNU General Public License 31 * You should have received a copy of the GNU General Public License
32* along with this program. If not, see <http://www.gnu.org/licenses/>. 32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33* 33 *
34* 34 *
35*****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_procs"; 37const char *progname = "check_procs";
38const char *program_name = "check_procs"; /* Required for coreutils libs */ 38const char *program_name = "check_procs"; /* Required for coreutils libs */
39const char *copyright = "2000-2024"; 39const char *copyright = "2000-2024";
40const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
41 41
@@ -43,313 +43,288 @@ const char *email = "devel@monitoring-plugins.org";
43#include "utils.h" 43#include "utils.h"
44#include "utils_cmd.h" 44#include "utils_cmd.h"
45#include "regex.h" 45#include "regex.h"
46#include "states.h"
47#include "check_procs.d/config.h"
46 48
47#include <pwd.h> 49#include <pwd.h>
48#include <errno.h> 50#include <errno.h>
49 51
50#ifdef HAVE_SYS_STAT_H 52#ifdef HAVE_SYS_STAT_H
51#include <sys/stat.h> 53# include <sys/stat.h>
52#endif 54#endif
53 55
54static int process_arguments (int /*argc*/, char ** /*argv*/); 56typedef struct {
55static int validate_arguments (void); 57 int errorcode;
56static int convert_to_seconds (char * /*etime*/); 58 check_procs_config config;
57static void print_help (void); 59} check_procs_config_wrapper;
58void print_usage (void); 60static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
59 61static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/);
60static char *warning_range = NULL; 62
61static char *critical_range = NULL; 63static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/);
62static thresholds *procs_thresholds = NULL; 64static void print_help(void);
63 65void print_usage(void);
64static int options = 0; /* bitmask of filter criteria to test against */ 66
65#define ALL 1 67#define ALL 1
66#define STAT 2 68#define STAT 2
67#define PPID 4 69#define PPID 4
68#define USER 8 70#define USER 8
69#define PROG 16 71#define PROG 16
70#define ARGS 32 72#define ARGS 32
71#define VSZ 64 73#define VSZ 64
72#define RSS 128 74#define RSS 128
73#define PCPU 256 75#define PCPU 256
74#define ELAPSED 512 76#define ELAPSED 512
75#define EREG_ARGS 1024 77#define EREG_ARGS 1024
76#define EXCLUDE_PROGS 2048 78#define EXCLUDE_PROGS 2048
77 79
78#define KTHREAD_PARENT "kthreadd" /* the parent process of kernel threads: 80#define KTHREAD_PARENT \
79 ppid of procs are compared to pid of this proc*/ 81 "kthreadd" /* the parent process of kernel threads: \
80 82 ppid of procs are compared to pid of this proc*/
81/* Different metrics */
82char *metric_name;
83enum metric {
84 METRIC_PROCS,
85 METRIC_VSZ,
86 METRIC_RSS,
87 METRIC_CPU,
88 METRIC_ELAPSED
89};
90enum metric metric = METRIC_PROCS;
91 83
92static int verbose = 0; 84static int verbose = 0;
93static int uid; 85
94static pid_t ppid; 86static int stat_exe(const pid_t pid, struct stat *buf) {
95static int vsz;
96static int rss;
97static float pcpu;
98static char *statopts;
99static char *prog;
100static char *exclude_progs;
101static char **exclude_progs_arr = NULL;
102static char exclude_progs_counter = 0;
103static char *args;
104static char *input_filename = NULL;
105static regex_t re_args;
106static char *fmt;
107static char *fails;
108static char tmp[MAX_INPUT_BUFFER];
109static int kthread_filter = 0;
110static int usepid = 0; /* whether to test for pid or /proc/pid/exe */
111
112static int
113stat_exe (const pid_t pid, struct stat *buf) {
114 char *path; 87 char *path;
115 int ret;
116 xasprintf(&path, "/proc/%d/exe", pid); 88 xasprintf(&path, "/proc/%d/exe", pid);
117 ret = stat(path, buf); 89 int ret = stat(path, buf);
118 free(path); 90 free(path);
119 return ret; 91 return ret;
120} 92}
121 93
122 94int main(int argc, char **argv) {
123int 95 setlocale(LC_ALL, "");
124main (int argc, char **argv)
125{
126 char *input_buffer;
127 char *input_line;
128 char *procprog;
129
130 pid_t mypid = 0;
131 pid_t myppid = 0;
132 struct stat statbuf;
133 dev_t mydev = 0;
134 ino_t myino = 0;
135 int procuid = 0;
136 pid_t procpid = 0;
137 pid_t procppid = 0;
138 pid_t kthread_ppid = 0;
139 int procvsz = 0;
140 int procrss = 0;
141 int procseconds = 0;
142 float procpcpu = 0;
143 char procstat[8];
144 char procetime[MAX_INPUT_BUFFER] = { '\0' };
145 char *procargs;
146
147 const char *zombie = "Z";
148
149 int resultsum = 0; /* bitmask of the filter criteria met by a process */
150 int found = 0; /* counter for number of lines returned in `ps` output */
151 int procs = 0; /* counter for number of processes meeting filter criteria */
152 int pos; /* number of spaces before 'args' in `ps` output */
153 int cols; /* number of columns in ps output */
154 int expected_cols = PS_COLS - 1;
155 int warn = 0; /* number of processes in warn state */
156 int crit = 0; /* number of processes in crit state */
157 int i = 0;
158 int result = STATE_UNKNOWN;
159 int ret = 0;
160 output chld_out, chld_err;
161
162 setlocale (LC_ALL, "");
163 bindtextdomain (PACKAGE, LOCALEDIR);
164 textdomain (PACKAGE);
165 setlocale(LC_NUMERIC, "POSIX"); 96 setlocale(LC_NUMERIC, "POSIX");
166 97 bindtextdomain(PACKAGE, LOCALEDIR);
167 input_buffer = malloc (MAX_INPUT_BUFFER); 98 textdomain(PACKAGE);
168 procprog = malloc (MAX_INPUT_BUFFER);
169
170 xasprintf (&metric_name, "PROCS");
171 metric = METRIC_PROCS;
172 99
173 /* Parse extra opts if any */ 100 /* Parse extra opts if any */
174 argv=np_extra_opts (&argc, argv, progname); 101 argv = np_extra_opts(&argc, argv, progname);
102
103 check_procs_config_wrapper tmp_config = process_arguments(argc, argv);
104 if (tmp_config.errorcode == ERROR) {
105 usage4(_("Could not parse arguments"));
106 }
175 107
176 if (process_arguments (argc, argv) == ERROR) 108 check_procs_config config = tmp_config.config;
177 usage4 (_("Could not parse arguments"));
178 109
179 /* find ourself */ 110 /* find ourself */
180 mypid = getpid(); 111 pid_t mypid = getpid();
181 myppid = getppid(); 112 pid_t myppid = getppid();
182 if (usepid || stat_exe(mypid, &statbuf) == -1) { 113 dev_t mydev = 0;
114 ino_t myino = 0;
115 struct stat statbuf;
116 if (config.usepid || stat_exe(mypid, &statbuf) == -1) {
183 /* usepid might have been set by -T */ 117 /* usepid might have been set by -T */
184 usepid = 1; 118 config.usepid = true;
185 } else { 119 } else {
186 usepid = 0; 120 config.usepid = false;
187 mydev = statbuf.st_dev; 121 mydev = statbuf.st_dev;
188 myino = statbuf.st_ino; 122 myino = statbuf.st_ino;
189 } 123 }
190 124
191 /* Set signal handling and alarm timeout */ 125 /* Set signal handling and alarm timeout */
192 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 126 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
193 die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); 127 die(STATE_UNKNOWN, _("Cannot catch SIGALRM"));
194 } 128 }
195 (void) alarm ((unsigned) timeout_interval); 129 (void)alarm(timeout_interval);
196 130
197 if (verbose >= 2) 131 if (verbose >= 2) {
198 printf (_("CMD: %s\n"), PS_COMMAND); 132 printf(_("CMD: %s\n"), PS_COMMAND);
133 }
199 134
200 if (input_filename == NULL) { 135 output chld_out;
201 result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); 136 output chld_err;
137 mp_state_enum result = STATE_UNKNOWN;
138 if (config.input_filename == NULL) {
139 result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0);
202 if (chld_err.lines > 0) { 140 if (chld_err.lines > 0) {
203 printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); 141 printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]);
204 exit(STATE_WARNING); 142 exit(STATE_WARNING);
205 } 143 }
206 } else { 144 } else {
207 result = cmd_file_read( input_filename, &chld_out, 0); 145 result = cmd_file_read(config.input_filename, &chld_out, 0);
208 } 146 }
209 147
148 int pos; /* number of spaces before 'args' in `ps` output */
149 uid_t procuid = 0;
150 pid_t procpid = 0;
151 pid_t procppid = 0;
152 pid_t kthread_ppid = 0;
153 int warn = 0; /* number of processes in warn state */
154 int crit = 0; /* number of processes in crit state */
155 int procvsz = 0;
156 int procrss = 0;
157 int procseconds = 0;
158 float procpcpu = 0;
159 char procstat[8];
160 char procetime[MAX_INPUT_BUFFER] = {'\0'};
161 int resultsum = 0; /* bitmask of the filter criteria met by a process */
162 int found = 0; /* counter for number of lines returned in `ps` output */
163 int procs = 0; /* counter for number of processes meeting filter criteria */
164 char *input_buffer = malloc(MAX_INPUT_BUFFER);
165 char *procprog = malloc(MAX_INPUT_BUFFER);
166 const int expected_cols = PS_COLS - 1;
167
210 /* flush first line: j starts at 1 */ 168 /* flush first line: j starts at 1 */
211 for (size_t j = 1; j < chld_out.lines; j++) { 169 for (size_t j = 1; j < chld_out.lines; j++) {
212 input_line = chld_out.line[j]; 170 char *input_line = chld_out.line[j];
213 171
214 if (verbose >= 3) 172 if (verbose >= 3) {
215 printf ("%s", input_line); 173 printf("%s", input_line);
174 }
216 175
217 strcpy (procprog, ""); 176 strcpy(procprog, "");
218 xasprintf (&procargs, "%s", ""); 177 char *procargs;
178 xasprintf(&procargs, "%s", "");
219 179
220 cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); 180 /* number of columns in ps output */
181 int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST);
221 182
222 /* Zombie processes do not give a procprog command */ 183 /* Zombie processes do not give a procprog command */
223 if ( cols < expected_cols && strstr(procstat, zombie) ) { 184 const char *zombie = "Z";
185 if (cols < expected_cols && strstr(procstat, zombie)) {
224 cols = expected_cols; 186 cols = expected_cols;
225 } 187 }
226 if ( cols >= expected_cols ) { 188 if (cols >= expected_cols) {
227 resultsum = 0; 189 resultsum = 0;
228 xasprintf (&procargs, "%s", input_line + pos); 190 xasprintf(&procargs, "%s", input_line + pos);
229 strip (procargs); 191 strip(procargs);
230 192
231 /* Some ps return full pathname for command. This removes path */ 193 /* Some ps return full pathname for command. This removes path */
232 strcpy(procprog, base_name(procprog)); 194 strcpy(procprog, base_name(procprog));
233 195
234 /* we need to convert the elapsed time to seconds */ 196 /* we need to convert the elapsed time to seconds */
235 procseconds = convert_to_seconds(procetime); 197 procseconds = convert_to_seconds(procetime, config.metric);
236 198
237 if (verbose >= 3) 199 if (verbose >= 3) {
238 printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 200 printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procs, procuid, procvsz,
239 procs, procuid, procvsz, procrss, 201 procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs);
240 procpid, procppid, procpcpu, procstat, 202 }
241 procetime, procprog, procargs);
242 203
243 /* Ignore self */ 204 /* Ignore self */
244 if ((usepid && mypid == procpid) || 205 int ret = 0;
245 ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || 206 if ((config.usepid && mypid == procpid) ||
246 (ret == -1 && errno == ENOENT)) 207 (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) ||
247 ) { 208 (ret == -1 && errno == ENOENT))) {
248 if (verbose >= 3) 209 if (verbose >= 3) {
249 printf("not considering - is myself or gone\n"); 210 printf("not considering - is myself or gone\n");
211 }
250 continue; 212 continue;
251 } 213 }
252 /* Ignore parent*/ 214 /* Ignore parent*/
253 else if (myppid == procpid) { 215 if (myppid == procpid) {
254 if (verbose >= 3) 216 if (verbose >= 3) {
255 printf("not considering - is parent\n"); 217 printf("not considering - is parent\n");
218 }
256 continue; 219 continue;
257 } 220 }
258 221
259 /* Ignore our own children */ 222 /* Ignore our own children */
260 if (procppid == mypid) { 223 if (procppid == mypid) {
261 if (verbose >= 3) 224 if (verbose >= 3) {
262 printf("not considering - is our child\n"); 225 printf("not considering - is our child\n");
226 }
263 continue; 227 continue;
264 } 228 }
265 229
266 /* Ignore excluded processes by name */ 230 /* Ignore excluded processes by name */
267 if(options & EXCLUDE_PROGS) { 231 if (config.options & EXCLUDE_PROGS) {
268 int found = 0; 232 bool found = false;
269 int i = 0; 233 for (int i = 0; i < (config.exclude_progs_counter); i++) {
270 234 if (!strcmp(procprog, config.exclude_progs_arr[i])) {
271 for(i=0; i < (exclude_progs_counter); i++) { 235 found = true;
272 if(!strcmp(procprog, exclude_progs_arr[i])) { 236 }
273 found = 1; 237 }
274 } 238 if (!found) {
275 } 239 resultsum |= EXCLUDE_PROGS;
276 if(found == 0) { 240 } else {
277 resultsum |= EXCLUDE_PROGS; 241 if (verbose >= 3) {
278 } else 242 printf("excluding - by ignorelist\n");
279 { 243 }
280 if(verbose >= 3) 244 }
281 printf("excluding - by ignorelist\n");
282 }
283 } 245 }
284 246
285 /* filter kernel threads (children of KTHREAD_PARENT)*/ 247 /* filter kernel threads (children of KTHREAD_PARENT)*/
286 /* TODO adapt for other OSes than GNU/Linux 248 /* TODO adapt for other OSes than GNU/Linux
287 sorry for not doing that, but I've no other OSes to test :-( */ 249 sorry for not doing that, but I've no other OSes to test :-( */
288 if (kthread_filter == 1) { 250 if (config.kthread_filter) {
289 /* get pid KTHREAD_PARENT */ 251 /* get pid KTHREAD_PARENT */
290 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) 252 if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) {
291 kthread_ppid = procpid; 253 kthread_ppid = procpid;
254 }
292 255
293 if (kthread_ppid == procppid) { 256 if (kthread_ppid == procppid) {
294 if (verbose >= 2) 257 if (verbose >= 2) {
295 printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); 258 printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs);
259 }
296 continue; 260 continue;
297 } 261 }
298 } 262 }
299 263
300 if ((options & STAT) && (strstr (procstat, statopts))) 264 if ((config.options & STAT) && (strstr(procstat, config.statopts))) {
301 resultsum |= STAT; 265 resultsum |= STAT;
302 if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) 266 }
267 if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) {
303 resultsum |= ARGS; 268 resultsum |= ARGS;
304 if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) 269 }
270 if ((config.options & EREG_ARGS) && procargs && (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) {
305 resultsum |= EREG_ARGS; 271 resultsum |= EREG_ARGS;
306 if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) 272 }
273 if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) {
307 resultsum |= PROG; 274 resultsum |= PROG;
308 if ((options & PPID) && (procppid == ppid)) 275 }
276 if ((config.options & PPID) && (procppid == config.ppid)) {
309 resultsum |= PPID; 277 resultsum |= PPID;
310 if ((options & USER) && (procuid == uid)) 278 }
279 if ((config.options & USER) && (procuid == config.uid)) {
311 resultsum |= USER; 280 resultsum |= USER;
312 if ((options & VSZ) && (procvsz >= vsz)) 281 }
282 if ((config.options & VSZ) && (procvsz >= config.vsz)) {
313 resultsum |= VSZ; 283 resultsum |= VSZ;
314 if ((options & RSS) && (procrss >= rss)) 284 }
285 if ((config.options & RSS) && (procrss >= config.rss)) {
315 resultsum |= RSS; 286 resultsum |= RSS;
316 if ((options & PCPU) && (procpcpu >= pcpu)) 287 }
288 if ((config.options & PCPU) && (procpcpu >= config.pcpu)) {
317 resultsum |= PCPU; 289 resultsum |= PCPU;
290 }
318 291
319 found++; 292 found++;
320 293
321 /* Next line if filters not matched */ 294 /* Next line if filters not matched */
322 if (!(options == resultsum || options == ALL)) 295 if (!(config.options == resultsum || config.options == ALL)) {
323 continue; 296 continue;
297 }
324 298
325 procs++; 299 procs++;
326 if (verbose >= 2) { 300 if (verbose >= 2) {
327 printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", 301 printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procuid, procvsz,
328 procuid, procvsz, procrss, 302 procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs);
329 procpid, procppid, procpcpu, procstat,
330 procetime, procprog, procargs);
331 } 303 }
332 304
333 if (metric == METRIC_VSZ) 305 mp_state_enum temporary_result = STATE_OK;
334 i = get_status ((double)procvsz, procs_thresholds); 306 if (config.metric == METRIC_VSZ) {
335 else if (metric == METRIC_RSS) 307 temporary_result = get_status((double)procvsz, config.procs_thresholds);
336 i = get_status ((double)procrss, procs_thresholds); 308 } else if (config.metric == METRIC_RSS) {
309 temporary_result = get_status((double)procrss, config.procs_thresholds);
310 }
337 /* TODO? float thresholds for --metric=CPU */ 311 /* TODO? float thresholds for --metric=CPU */
338 else if (metric == METRIC_CPU) 312 else if (config.metric == METRIC_CPU) {
339 i = get_status (procpcpu, procs_thresholds); 313 temporary_result = get_status(procpcpu, config.procs_thresholds);
340 else if (metric == METRIC_ELAPSED) 314 } else if (config.metric == METRIC_ELAPSED) {
341 i = get_status ((double)procseconds, procs_thresholds); 315 temporary_result = get_status((double)procseconds, config.procs_thresholds);
316 }
342 317
343 if (metric != METRIC_PROCS) { 318 if (config.metric != METRIC_PROCS) {
344 if (i == STATE_WARNING) { 319 if (temporary_result == STATE_WARNING) {
345 warn++; 320 warn++;
346 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 321 xasprintf(&config.fails, "%s%s%s", config.fails, (strcmp(config.fails, "") ? ", " : ""), procprog);
347 result = max_state (result, i); 322 result = max_state(result, temporary_result);
348 } 323 }
349 if (i == STATE_CRITICAL) { 324 if (temporary_result == STATE_CRITICAL) {
350 crit++; 325 crit++;
351 xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); 326 xasprintf(&config.fails, "%s%s%s", config.fails, (strcmp(config.fails, "") ? ", " : ""), procprog);
352 result = max_state (result, i); 327 result = max_state(result, temporary_result);
353 } 328 }
354 } 329 }
355 } 330 }
@@ -359,339 +334,350 @@ main (int argc, char **argv)
359 } 334 }
360 } 335 }
361 336
362 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ 337 if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */
363 printf (_("Unable to read output\n")); 338 printf(_("Unable to read output\n"));
364 return STATE_UNKNOWN; 339 return STATE_UNKNOWN;
365 } 340 }
366 341
367 if ( result == STATE_UNKNOWN ) 342 if (result == STATE_UNKNOWN) {
368 result = STATE_OK; 343 result = STATE_OK;
344 }
369 345
370 /* Needed if procs found, but none match filter */ 346 /* Needed if procs found, but none match filter */
371 if ( metric == METRIC_PROCS ) { 347 if (config.metric == METRIC_PROCS) {
372 result = max_state (result, get_status ((double)procs, procs_thresholds) ); 348 result = max_state(result, get_status((double)procs, config.procs_thresholds));
373 } 349 }
374 350
375 if ( result == STATE_OK ) { 351 if (result == STATE_OK) {
376 printf ("%s %s: ", metric_name, _("OK")); 352 printf("%s %s: ", config.metric_name, _("OK"));
377 } else if (result == STATE_WARNING) { 353 } else if (result == STATE_WARNING) {
378 printf ("%s %s: ", metric_name, _("WARNING")); 354 printf("%s %s: ", config.metric_name, _("WARNING"));
379 if ( metric != METRIC_PROCS ) { 355 if (config.metric != METRIC_PROCS) {
380 printf (_("%d warn out of "), warn); 356 printf(_("%d warn out of "), warn);
381 } 357 }
382 } else if (result == STATE_CRITICAL) { 358 } else if (result == STATE_CRITICAL) {
383 printf ("%s %s: ", metric_name, _("CRITICAL")); 359 printf("%s %s: ", config.metric_name, _("CRITICAL"));
384 if (metric != METRIC_PROCS) { 360 if (config.metric != METRIC_PROCS) {
385 printf (_("%d crit, %d warn out of "), crit, warn); 361 printf(_("%d crit, %d warn out of "), crit, warn);
386 } 362 }
387 } 363 }
388 printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); 364 printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs);
389 365
390 if (strcmp(fmt,"") != 0) { 366 if (strcmp(config.fmt, "") != 0) {
391 printf (_(" with %s"), fmt); 367 printf(_(" with %s"), config.fmt);
392 } 368 }
393 369
394 if ( verbose >= 1 && strcmp(fails,"") ) 370 if (verbose >= 1 && strcmp(config.fails, "")) {
395 printf (" [%s]", fails); 371 printf(" [%s]", config.fails);
372 }
396 373
397 if (metric == METRIC_PROCS) 374 if (config.metric == METRIC_PROCS) {
398 printf (" | procs=%d;%s;%s;0;", procs, 375 printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "",
399 warning_range ? warning_range : "", 376 config.critical_range ? config.critical_range : "");
400 critical_range ? critical_range : ""); 377 } else {
401 else 378 printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit);
402 printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); 379 }
403 380
404 printf ("\n"); 381 printf("\n");
405 return result; 382 exit(result);
406} 383}
407 384
408
409
410/* process command-line arguments */ 385/* process command-line arguments */
411int 386check_procs_config_wrapper process_arguments(int argc, char **argv) {
412process_arguments (int argc, char **argv) 387 static struct option longopts[] = {{"warning", required_argument, 0, 'w'},
413{ 388 {"critical", required_argument, 0, 'c'},
414 int c = 1; 389 {"metric", required_argument, 0, 'm'},
415 char *user; 390 {"timeout", required_argument, 0, 't'},
416 struct passwd *pw; 391 {"status", required_argument, 0, 's'},
417 int option = 0; 392 {"ppid", required_argument, 0, 'p'},
418 int err; 393 {"user", required_argument, 0, 'u'},
419 int cflags = REG_NOSUB | REG_EXTENDED; 394 {"command", required_argument, 0, 'C'},
420 char errbuf[MAX_INPUT_BUFFER]; 395 {"vsz", required_argument, 0, 'z'},
421 char *temp_string; 396 {"rss", required_argument, 0, 'r'},
422 int i=0; 397 {"pcpu", required_argument, 0, 'P'},
423 static struct option longopts[] = { 398 {"elapsed", required_argument, 0, 'e'},
424 {"warning", required_argument, 0, 'w'}, 399 {"argument-array", required_argument, 0, 'a'},
425 {"critical", required_argument, 0, 'c'}, 400 {"help", no_argument, 0, 'h'},
426 {"metric", required_argument, 0, 'm'}, 401 {"version", no_argument, 0, 'V'},
427 {"timeout", required_argument, 0, 't'}, 402 {"verbose", no_argument, 0, 'v'},
428 {"status", required_argument, 0, 's'}, 403 {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1},
429 {"ppid", required_argument, 0, 'p'}, 404 {"input-file", required_argument, 0, CHAR_MAX + 2},
430 {"user", required_argument, 0, 'u'}, 405 {"no-kthreads", required_argument, 0, 'k'},
431 {"command", required_argument, 0, 'C'}, 406 {"traditional-filter", no_argument, 0, 'T'},
432 {"vsz", required_argument, 0, 'z'}, 407 {"exclude-process", required_argument, 0, 'X'},
433 {"rss", required_argument, 0, 'r'}, 408 {0, 0, 0, 0}};
434 {"pcpu", required_argument, 0, 'P'}, 409
435 {"elapsed", required_argument, 0, 'e'}, 410 for (int index = 1; index < argc; index++) {
436 {"argument-array", required_argument, 0, 'a'}, 411 if (strcmp("-to", argv[index]) == 0) {
437 {"help", no_argument, 0, 'h'}, 412 strcpy(argv[index], "-t");
438 {"version", no_argument, 0, 'V'}, 413 }
439 {"verbose", no_argument, 0, 'v'}, 414 }
440 {"ereg-argument-array", required_argument, 0, CHAR_MAX+1},
441 {"input-file", required_argument, 0, CHAR_MAX+2},
442 {"no-kthreads", required_argument, 0, 'k'},
443 {"traditional-filter", no_argument, 0, 'T'},
444 {"exclude-process", required_argument, 0, 'X'},
445 {0, 0, 0, 0}
446 };
447 415
448 for (c = 1; c < argc; c++) 416 check_procs_config_wrapper result = {
449 if (strcmp ("-to", argv[c]) == 0) 417 .errorcode = OK,
450 strcpy (argv[c], "-t"); 418 .config = check_procs_config_init(),
419 };
451 420
452 while (1) { 421 while (true) {
453 c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", 422 int option = 0;
454 longopts, &option); 423 int option_index = getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option);
455 424
456 if (c == -1 || c == EOF) 425 if (option_index == -1 || option_index == EOF) {
457 break; 426 break;
427 }
458 428
459 switch (c) { 429 switch (option_index) {
460 case '?': /* help */ 430 case '?': /* help */
461 usage5 (); 431 usage5();
462 case 'h': /* help */ 432 case 'h': /* help */
463 print_help (); 433 print_help();
464 exit (STATE_UNKNOWN); 434 exit(STATE_UNKNOWN);
465 case 'V': /* version */ 435 case 'V': /* version */
466 print_revision (progname, NP_VERSION); 436 print_revision(progname, NP_VERSION);
467 exit (STATE_UNKNOWN); 437 exit(STATE_UNKNOWN);
468 case 't': /* timeout period */ 438 case 't': /* timeout period */
469 if (!is_integer (optarg)) 439 if (!is_integer(optarg)) {
470 usage2 (_("Timeout interval must be a positive integer"), optarg); 440 usage2(_("Timeout interval must be a positive integer"), optarg);
471 else 441 } else {
472 timeout_interval = atoi (optarg); 442 timeout_interval = atoi(optarg);
443 }
473 break; 444 break;
474 case 'c': /* critical threshold */ 445 case 'c': /* critical threshold */
475 critical_range = optarg; 446 result.config.critical_range = optarg;
476 break; 447 break;
477 case 'w': /* warning threshold */ 448 case 'w': /* warning threshold */
478 warning_range = optarg; 449 result.config.warning_range = optarg;
479 break; 450 break;
480 case 'p': /* process id */ 451 case 'p': { /* process id */
481 if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { 452 static char tmp[MAX_INPUT_BUFFER];
482 xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); 453 if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) {
483 options |= PPID; 454 xasprintf(&result.config.fmt, "%s%sPPID = %d", (result.config.fmt ? result.config.fmt : ""),
455 (result.config.options ? ", " : ""), result.config.ppid);
456 result.config.options |= PPID;
484 break; 457 break;
485 } 458 }
486 usage4 (_("Parent Process ID must be an integer!")); 459 usage4(_("Parent Process ID must be an integer!"));
487 case 's': /* status */ 460 }
488 if (statopts) 461 case 's': /* status */
462 if (result.config.statopts) {
489 break; 463 break;
490 else 464 } else {
491 statopts = optarg; 465 result.config.statopts = optarg;
492 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 466 }
493 options |= STAT; 467 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), (result.config.fmt ? result.config.fmt : ""),
468 (result.config.options ? ", " : ""), result.config.statopts);
469 result.config.options |= STAT;
494 break; 470 break;
495 case 'u': /* user or user id */ 471 case 'u': /* user or user id */ {
496 if (is_integer (optarg)) { 472 struct passwd *pw;
497 uid = atoi (optarg); 473 if (is_integer(optarg)) {
498 pw = getpwuid ((uid_t) uid); 474 result.config.uid = atoi(optarg);
475 pw = getpwuid(result.config.uid);
499 /* check to be sure user exists */ 476 /* check to be sure user exists */
500 if (pw == NULL) 477 if (pw == NULL) {
501 usage2 (_("UID was not found"), optarg); 478 usage2(_("UID was not found"), optarg);
502 } 479 }
503 else { 480 } else {
504 pw = getpwnam (optarg); 481 pw = getpwnam(optarg);
505 /* check to be sure user exists */ 482 /* check to be sure user exists */
506 if (pw == NULL) 483 if (pw == NULL) {
507 usage2 (_("User name was not found"), optarg); 484 usage2(_("User name was not found"), optarg);
485 }
508 /* then get uid */ 486 /* then get uid */
509 uid = pw->pw_uid; 487 result.config.uid = pw->pw_uid;
510 } 488 }
511 user = pw->pw_name; 489
512 xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), 490 char *user = pw->pw_name;
513 uid, user); 491 xasprintf(&result.config.fmt, "%s%sUID = %d (%s)", (result.config.fmt ? result.config.fmt : ""),
514 options |= USER; 492 (result.config.options ? ", " : ""), result.config.uid, user);
515 break; 493 result.config.options |= USER;
516 case 'C': /* command */ 494 } break;
495 case 'C': /* command */
517 /* TODO: allow this to be passed in with --metric */ 496 /* TODO: allow this to be passed in with --metric */
518 if (prog) 497 if (result.config.prog) {
519 break; 498 break;
520 else 499 } else {
521 prog = optarg; 500 result.config.prog = optarg;
522 xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 501 }
523 prog); 502 xasprintf(&result.config.fmt, _("%s%scommand name '%s'"), (result.config.fmt ? result.config.fmt : ""),
524 options |= PROG; 503 (result.config.options ? ", " : ""), result.config.prog);
504 result.config.options |= PROG;
525 break; 505 break;
526 case 'X': 506 case 'X':
527 if(exclude_progs) 507 if (result.config.exclude_progs) {
528 break; 508 break;
529 else 509 } else {
530 exclude_progs = optarg; 510 result.config.exclude_progs = optarg;
531 xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), 511 }
532 exclude_progs); 512 xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"), (result.config.fmt ? result.config.fmt : ""),
533 char *p = strtok(exclude_progs, ","); 513 (result.config.options ? ", " : ""), result.config.exclude_progs);
534 514 char *tmp_pointer = strtok(result.config.exclude_progs, ",");
535 while(p){ 515
536 exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); 516 while (tmp_pointer) {
537 exclude_progs_arr[exclude_progs_counter-1] = p; 517 result.config.exclude_progs_arr =
538 p = strtok(NULL, ","); 518 realloc(result.config.exclude_progs_arr, sizeof(char *) * ++result.config.exclude_progs_counter);
519 result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] = tmp_pointer;
520 tmp_pointer = strtok(NULL, ",");
539 } 521 }
540 522
541 options |= EXCLUDE_PROGS; 523 result.config.options |= EXCLUDE_PROGS;
542 break; 524 break;
543 case 'a': /* args (full path name with args) */ 525 case 'a': /* args (full path name with args) */
544 /* TODO: allow this to be passed in with --metric */ 526 /* TODO: allow this to be passed in with --metric */
545 if (args) 527 if (result.config.args) {
546 break; 528 break;
547 else 529 } else {
548 args = optarg; 530 result.config.args = optarg;
549 xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); 531 }
550 options |= ARGS; 532 xasprintf(&result.config.fmt, "%s%sargs '%s'", (result.config.fmt ? result.config.fmt : ""),
533 (result.config.options ? ", " : ""), result.config.args);
534 result.config.options |= ARGS;
551 break; 535 break;
552 case CHAR_MAX+1: 536 case CHAR_MAX + 1: {
553 err = regcomp(&re_args, optarg, cflags); 537 int cflags = REG_NOSUB | REG_EXTENDED;
538 int err = regcomp(&result.config.re_args, optarg, cflags);
554 if (err != 0) { 539 if (err != 0) {
555 regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); 540 char errbuf[MAX_INPUT_BUFFER];
556 die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 541 regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER);
542 die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf);
557 } 543 }
558 /* Strip off any | within the regex optarg */ 544 /* Strip off any | within the regex optarg */
559 temp_string = strdup(optarg); 545 char *temp_string = strdup(optarg);
560 while(temp_string[i]!='\0'){ 546 int index = 0;
561 if(temp_string[i]=='|') 547 while (temp_string[index] != '\0') {
562 temp_string[i]=','; 548 if (temp_string[index] == '|') {
563 i++; 549 temp_string[index] = ',';
564 } 550 }
565 xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); 551 index++;
566 options |= EREG_ARGS; 552 }
567 break; 553 xasprintf(&result.config.fmt, "%s%sregex args '%s'", (result.config.fmt ? result.config.fmt : ""),
568 case 'r': /* RSS */ 554 (result.config.options ? ", " : ""), temp_string);
569 if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { 555 result.config.options |= EREG_ARGS;
570 xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); 556 } break;
571 options |= RSS; 557 case 'r': { /* RSS */
558 static char tmp[MAX_INPUT_BUFFER];
559 if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) {
560 xasprintf(&result.config.fmt, "%s%sRSS >= %d", (result.config.fmt ? result.config.fmt : ""),
561 (result.config.options ? ", " : ""), result.config.rss);
562 result.config.options |= RSS;
572 break; 563 break;
573 } 564 }
574 usage4 (_("RSS must be an integer!")); 565 usage4(_("RSS must be an integer!"));
575 case 'z': /* VSZ */ 566 }
576 if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { 567 case 'z': { /* VSZ */
577 xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); 568 static char tmp[MAX_INPUT_BUFFER];
578 options |= VSZ; 569 if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) {
570 xasprintf(&result.config.fmt, "%s%sVSZ >= %d", (result.config.fmt ? result.config.fmt : ""),
571 (result.config.options ? ", " : ""), result.config.vsz);
572 result.config.options |= VSZ;
579 break; 573 break;
580 } 574 }
581 usage4 (_("VSZ must be an integer!")); 575 usage4(_("VSZ must be an integer!"));
582 case 'P': /* PCPU */ 576 }
577 case 'P': { /* PCPU */
583 /* TODO: -P 1.5.5 is accepted */ 578 /* TODO: -P 1.5.5 is accepted */
584 if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { 579 static char tmp[MAX_INPUT_BUFFER];
585 xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); 580 if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) {
586 options |= PCPU; 581 xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f", (result.config.fmt ? result.config.fmt : ""),
582 (result.config.options ? ", " : ""), result.config.pcpu);
583 result.config.options |= PCPU;
587 break; 584 break;
588 } 585 }
589 usage4 (_("PCPU must be a float!")); 586 usage4(_("PCPU must be a float!"));
587 }
590 case 'm': 588 case 'm':
591 xasprintf (&metric_name, "%s", optarg); 589 xasprintf(&result.config.metric_name, "%s", optarg);
592 if ( strcmp(optarg, "PROCS") == 0) { 590 if (strcmp(optarg, "PROCS") == 0) {
593 metric = METRIC_PROCS; 591 result.config.metric = METRIC_PROCS;
594 break; 592 break;
595 } 593 }
596 else if ( strcmp(optarg, "VSZ") == 0) { 594 if (strcmp(optarg, "VSZ") == 0) {
597 metric = METRIC_VSZ; 595 result.config.metric = METRIC_VSZ;
598 break; 596 break;
599 } 597 }
600 else if ( strcmp(optarg, "RSS") == 0 ) { 598 if (strcmp(optarg, "RSS") == 0) {
601 metric = METRIC_RSS; 599 result.config.metric = METRIC_RSS;
602 break; 600 break;
603 } 601 }
604 else if ( strcmp(optarg, "CPU") == 0 ) { 602 if (strcmp(optarg, "CPU") == 0) {
605 metric = METRIC_CPU; 603 result.config.metric = METRIC_CPU;
606 break; 604 break;
607 } 605 }
608 else if ( strcmp(optarg, "ELAPSED") == 0) { 606 if (strcmp(optarg, "ELAPSED") == 0) {
609 metric = METRIC_ELAPSED; 607 result.config.metric = METRIC_ELAPSED;
610 break; 608 break;
611 } 609 }
612 610
613 usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); 611 usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!"));
614 case 'k': /* linux kernel thread filter */ 612 case 'k': /* linux kernel thread filter */
615 kthread_filter = 1; 613 result.config.kthread_filter = true;
616 break; 614 break;
617 case 'v': /* command */ 615 case 'v': /* command */
618 verbose++; 616 verbose++;
619 break; 617 break;
620 case 'T': 618 case 'T':
621 usepid = 1; 619 result.config.usepid = true;
622 break; 620 break;
623 case CHAR_MAX+2: 621 case CHAR_MAX + 2:
624 input_filename = optarg; 622 result.config.input_filename = optarg;
625 break; 623 break;
626 } 624 }
627 } 625 }
628 626
629 c = optind; 627 int index = optind;
630 if ((! warning_range) && argv[c]) 628 if ((!result.config.warning_range) && argv[index]) {
631 warning_range = argv[c++]; 629 result.config.warning_range = argv[index++];
632 if ((! critical_range) && argv[c]) 630 }
633 critical_range = argv[c++]; 631 if ((!result.config.critical_range) && argv[index]) {
634 if (statopts == NULL && argv[c]) { 632 result.config.critical_range = argv[index++];
635 xasprintf (&statopts, "%s", argv[c++]); 633 }
636 xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); 634 if (result.config.statopts == NULL && argv[index]) {
637 options |= STAT; 635 xasprintf(&result.config.statopts, "%s", argv[index++]);
636 xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), (result.config.fmt ? result.config.fmt : ""),
637 (result.config.options ? ", " : ""), result.config.statopts);
638 result.config.options |= STAT;
638 } 639 }
639 640
640 /* this will abort in case of invalid ranges */ 641 /* this will abort in case of invalid ranges */
641 set_thresholds (&procs_thresholds, warning_range, critical_range); 642 set_thresholds(&result.config.procs_thresholds, result.config.warning_range, result.config.critical_range);
642 643
643 return validate_arguments (); 644 return validate_arguments(result);
644} 645}
645 646
647check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) {
648 if (config_wrapper.config.options == 0) {
649 config_wrapper.config.options = ALL;
650 }
646 651
652 if (config_wrapper.config.statopts == NULL) {
653 config_wrapper.config.statopts = strdup("");
654 }
647 655
648int 656 if (config_wrapper.config.prog == NULL) {
649validate_arguments () 657 config_wrapper.config.prog = strdup("");
650{ 658 }
651 if (options == 0)
652 options = ALL;
653
654 if (statopts==NULL)
655 statopts = strdup("");
656
657 if (prog==NULL)
658 prog = strdup("");
659 659
660 if (args==NULL) 660 if (config_wrapper.config.args == NULL) {
661 args = strdup(""); 661 config_wrapper.config.args = strdup("");
662 }
662 663
663 if (fmt==NULL) 664 if (config_wrapper.config.fmt == NULL) {
664 fmt = strdup(""); 665 config_wrapper.config.fmt = strdup("");
666 }
665 667
666 if (fails==NULL) 668 if (config_wrapper.config.fails == NULL) {
667 fails = strdup(""); 669 config_wrapper.config.fails = strdup("");
670 }
668 671
669 return options; 672 // return options;
673 return config_wrapper;
670} 674}
671 675
672
673/* convert the elapsed time to seconds */ 676/* convert the elapsed time to seconds */
674int 677int convert_to_seconds(char *etime, enum metric metric) {
675convert_to_seconds(char *etime) { 678 int hyphcnt = 0;
676 679 int coloncnt = 0;
677 char *ptr; 680 for (char *ptr = etime; *ptr != '\0'; ptr++) {
678 int total;
679
680 int hyphcnt;
681 int coloncnt;
682 int days;
683 int hours;
684 int minutes;
685 int seconds;
686
687 hyphcnt = 0;
688 coloncnt = 0;
689 days = 0;
690 hours = 0;
691 minutes = 0;
692 seconds = 0;
693
694 for (ptr = etime; *ptr != '\0'; ptr++) {
695 681
696 if (*ptr == '-') { 682 if (*ptr == '-') {
697 hyphcnt++; 683 hyphcnt++;
@@ -703,9 +689,12 @@ convert_to_seconds(char *etime) {
703 } 689 }
704 } 690 }
705 691
692 int days = 0;
693 int hours = 0;
694 int minutes = 0;
695 int seconds = 0;
706 if (hyphcnt > 0) { 696 if (hyphcnt > 0) {
707 sscanf(etime, "%d-%d:%d:%d", 697 sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds);
708 &days, &hours, &minutes, &seconds);
709 /* linux 2.6.5/2.6.6 reporting some processes with infinite 698 /* linux 2.6.5/2.6.6 reporting some processes with infinite
710 * elapsed times for some reason */ 699 * elapsed times for some reason */
711 if (days == 49710) { 700 if (days == 49710) {
@@ -713,135 +702,125 @@ convert_to_seconds(char *etime) {
713 } 702 }
714 } else { 703 } else {
715 if (coloncnt == 2) { 704 if (coloncnt == 2) {
716 sscanf(etime, "%d:%d:%d", 705 sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds);
717 &hours, &minutes, &seconds);
718 } else if (coloncnt == 1) { 706 } else if (coloncnt == 1) {
719 sscanf(etime, "%d:%d", 707 sscanf(etime, "%d:%d", &minutes, &seconds);
720 &minutes, &seconds);
721 } 708 }
722 } 709 }
723 710
724 total = (days * 86400) + 711 int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds;
725 (hours * 3600) +
726 (minutes * 60) +
727 seconds;
728 712
729 if (verbose >= 3 && metric == METRIC_ELAPSED) { 713 if (verbose >= 3 && metric == METRIC_ELAPSED) {
730 printf("seconds: %d\n", total); 714 printf("seconds: %d\n", total);
731 } 715 }
732 return total; 716 return total;
733} 717}
734 718
719void print_help(void) {
720 print_revision(progname, NP_VERSION);
735 721
736void 722 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
737print_help (void) 723 printf(COPYRIGHT, copyright, email);
738{
739 print_revision (progname, NP_VERSION);
740
741 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
742 printf (COPYRIGHT, copyright, email);
743 724
744 printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); 725 printf("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified"));
745 printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); 726 printf("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number"));
746 printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); 727 printf("%s\n", _("of processes. Search filters can be applied to limit the processes to check."));
747 728
748 printf ("\n\n"); 729 printf("\n\n");
749 730
750 printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); 731 printf("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)"));
751 printf ("%s\n", _("are excluded from any checks to prevent false positives.")); 732 printf("%s\n", _("are excluded from any checks to prevent false positives."));
752 733
753 printf ("\n\n"); 734 printf("\n\n");
754 735
755 print_usage (); 736 print_usage();
756 737
757 printf (UT_HELP_VRSN); 738 printf(UT_HELP_VRSN);
758 printf (UT_EXTRA_OPTS); 739 printf(UT_EXTRA_OPTS);
759 printf (" %s\n", "-w, --warning=RANGE"); 740 printf(" %s\n", "-w, --warning=RANGE");
760 printf (" %s\n", _("Generate warning state if metric is outside this range")); 741 printf(" %s\n", _("Generate warning state if metric is outside this range"));
761 printf (" %s\n", "-c, --critical=RANGE"); 742 printf(" %s\n", "-c, --critical=RANGE");
762 printf (" %s\n", _("Generate critical state if metric is outside this range")); 743 printf(" %s\n", _("Generate critical state if metric is outside this range"));
763 printf (" %s\n", "-m, --metric=TYPE"); 744 printf(" %s\n", "-m, --metric=TYPE");
764 printf (" %s\n", _("Check thresholds against metric. Valid types:")); 745 printf(" %s\n", _("Check thresholds against metric. Valid types:"));
765 printf (" %s\n", _("PROCS - number of processes (default)")); 746 printf(" %s\n", _("PROCS - number of processes (default)"));
766 printf (" %s\n", _("VSZ - virtual memory size")); 747 printf(" %s\n", _("VSZ - virtual memory size"));
767 printf (" %s\n", _("RSS - resident set memory size")); 748 printf(" %s\n", _("RSS - resident set memory size"));
768 printf (" %s\n", _("CPU - percentage CPU")); 749 printf(" %s\n", _("CPU - percentage CPU"));
769/* only linux etime is support currently */ 750/* only linux etime is support currently */
770#if defined( __linux__ ) 751#if defined(__linux__)
771 printf (" %s\n", _("ELAPSED - time elapsed in seconds")); 752 printf(" %s\n", _("ELAPSED - time elapsed in seconds"));
772#endif /* defined(__linux__) */ 753#endif /* defined(__linux__) */
773 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 754 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
774 755
775 printf (" %s\n", "-v, --verbose"); 756 printf(" %s\n", "-v, --verbose");
776 printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); 757 printf(" %s\n", _("Extra information. Up to 3 verbosity levels"));
777 758
778 printf (" %s\n", "-T, --traditional"); 759 printf(" %s\n", "-T, --traditional");
779 printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); 760 printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe"));
780 761
781 printf ("\n"); 762 printf("\n");
782 printf ("%s\n", "Filters:"); 763 printf("%s\n", "Filters:");
783 printf (" %s\n", "-s, --state=STATUSFLAGS"); 764 printf(" %s\n", "-s, --state=STATUSFLAGS");
784 printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); 765 printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or"));
785 printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); 766 printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,"));
786 printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); 767 printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command)."));
787 printf (" %s\n", "-p, --ppid=PPID"); 768 printf(" %s\n", "-p, --ppid=PPID");
788 printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); 769 printf(" %s\n", _("Only scan for children of the parent process ID indicated."));
789 printf (" %s\n", "-z, --vsz=VSZ"); 770 printf(" %s\n", "-z, --vsz=VSZ");
790 printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); 771 printf(" %s\n", _("Only scan for processes with VSZ higher than indicated."));
791 printf (" %s\n", "-r, --rss=RSS"); 772 printf(" %s\n", "-r, --rss=RSS");
792 printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); 773 printf(" %s\n", _("Only scan for processes with RSS higher than indicated."));
793 printf (" %s\n", "-P, --pcpu=PCPU"); 774 printf(" %s\n", "-P, --pcpu=PCPU");
794 printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); 775 printf(" %s\n", _("Only scan for processes with PCPU higher than indicated."));
795 printf (" %s\n", "-u, --user=USER"); 776 printf(" %s\n", "-u, --user=USER");
796 printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); 777 printf(" %s\n", _("Only scan for processes with user name or ID indicated."));
797 printf (" %s\n", "-a, --argument-array=STRING"); 778 printf(" %s\n", "-a, --argument-array=STRING");
798 printf (" %s\n", _("Only scan for processes with args that contain STRING.")); 779 printf(" %s\n", _("Only scan for processes with args that contain STRING."));
799 printf (" %s\n", "--ereg-argument-array=STRING"); 780 printf(" %s\n", "--ereg-argument-array=STRING");
800 printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); 781 printf(" %s\n", _("Only scan for processes with args that contain the regex STRING."));
801 printf (" %s\n", "-C, --command=COMMAND"); 782 printf(" %s\n", "-C, --command=COMMAND");
802 printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); 783 printf(" %s\n", _("Only scan for exact matches of COMMAND (without path)."));
803 printf (" %s\n", "-X, --exclude-process"); 784 printf(" %s\n", "-X, --exclude-process");
804 printf (" %s\n", _("Exclude processes which match this comma separated list")); 785 printf(" %s\n", _("Exclude processes which match this comma separated list"));
805 printf (" %s\n", "-k, --no-kthreads"); 786 printf(" %s\n", "-k, --no-kthreads");
806 printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); 787 printf(" %s\n", _("Only scan for non kernel threads (works on Linux only)."));
807 788
808 printf(_("\n\ 789 printf(_("\n\
809RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ 790RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\
810specified 'max:min', a warning status will be generated if the\n\ 791specified 'max:min', a warning status will be generated if the\n\
811count is inside the specified range\n\n")); 792count is inside the specified range\n\n"));
812 793
813 printf(_("\ 794 printf(_("\
814This plugin checks the number of currently running processes and\n\ 795This plugin checks the number of currently running processes and\n\
815generates WARNING or CRITICAL states if the process count is outside\n\ 796generates WARNING or CRITICAL states if the process count is outside\n\
816the specified threshold ranges. The process count can be filtered by\n\ 797the specified threshold ranges. The process count can be filtered by\n\
817process owner, parent process PID, current state (e.g., 'Z'), or may\n\ 798process owner, parent process PID, current state (e.g., 'Z'), or may\n\
818be the total number of running processes\n\n")); 799be the total number of running processes\n\n"));
819 800
820 printf ("%s\n", _("Examples:")); 801 printf("%s\n", _("Examples:"));
821 printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); 802 printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry");
822 printf (" %s\n", _("Warning if not two processes with command name portsentry.")); 803 printf(" %s\n", _("Warning if not two processes with command name portsentry."));
823 printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); 804 printf(" %s\n\n", _("Critical if < 2 or > 1024 processes"));
824 printf (" %s\n", "check_procs -c 1: -C sshd"); 805 printf(" %s\n", "check_procs -c 1: -C sshd");
825 printf (" %s\n", _("Critical if not at least 1 process with command sshd")); 806 printf(" %s\n", _("Critical if not at least 1 process with command sshd"));
826 printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); 807 printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd");
827 printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); 808 printf(" %s\n", _("Warning if > 1024 processes with command name sshd."));
828 printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); 809 printf(" %s\n\n", _("Critical if < 1 processes with command name sshd."));
829 printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); 810 printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root");
830 printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); 811 printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing"));
831 printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); 812 printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root"));
832 printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); 813 printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ");
833 printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); 814 printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K"));
834 printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); 815 printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU");
835 printf (" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); 816 printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%"));
836 817
837 printf (UT_SUPPORT); 818 printf(UT_SUPPORT);
838} 819}
839 820
840void 821void print_usage(void) {
841print_usage (void) 822 printf("%s\n", _("Usage:"));
842{ 823 printf("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname);
843 printf ("%s\n", _("Usage:")); 824 printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
844 printf ("%s -w <range> -c <range> [-m metric] [-s state] [-p ppid]\n", progname); 825 printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
845 printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n");
846 printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n");
847} 826}
diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h
new file mode 100644
index 00000000..e32ca066
--- /dev/null
+++ b/plugins/check_procs.d/config.h
@@ -0,0 +1,75 @@
1#pragma once
2
3#include "../../config.h"
4#include "regex.h"
5#include "thresholds.h"
6#include <stddef.h>
7#include <string.h>
8#include <sys/types.h>
9
10enum metric {
11 METRIC_PROCS,
12 METRIC_VSZ,
13 METRIC_RSS,
14 METRIC_CPU,
15 METRIC_ELAPSED
16};
17
18typedef struct {
19 int options; /* bitmask of filter criteria to test against */
20 enum metric metric;
21 char *metric_name;
22 char *input_filename;
23 char *prog;
24 char *args;
25 char *fmt;
26 char *fails;
27 char *exclude_progs;
28 char **exclude_progs_arr;
29 char exclude_progs_counter;
30 regex_t re_args;
31
32 bool kthread_filter;
33 bool usepid; /* whether to test for pid or /proc/pid/exe */
34 uid_t uid;
35 pid_t ppid;
36 int vsz;
37 int rss;
38 float pcpu;
39 char *statopts;
40
41 char *warning_range;
42 char *critical_range;
43 thresholds *procs_thresholds;
44} check_procs_config;
45
46check_procs_config check_procs_config_init() {
47 check_procs_config tmp = {
48 .options = 0,
49 .metric = METRIC_PROCS,
50 .metric_name = strdup("PROCS"),
51 .input_filename = NULL,
52 .prog = NULL,
53 .args = NULL,
54 .fmt = NULL,
55 .fails = NULL,
56 .exclude_progs = NULL,
57 .exclude_progs_arr = NULL,
58 .exclude_progs_counter = 0,
59 .re_args = {0},
60
61 .kthread_filter = false,
62 .usepid = false,
63 .uid = 0,
64 .ppid = 0,
65 .vsz = 0,
66 .rss = 0,
67 .pcpu = 0,
68 .statopts = NULL,
69
70 .warning_range = NULL,
71 .critical_range = NULL,
72 .procs_thresholds = NULL,
73 };
74 return tmp;
75}
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c
index c1d8e2dd..a5a7afe8 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -32,716 +32,494 @@ const char *progname = "check_snmp";
32const char *copyright = "1999-2024"; 32const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "runcmd.h" 36#include "./runcmd.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_cmd.h" 38#include "../lib/states.h"
39 39
40#define DEFAULT_COMMUNITY "public" 40#include "../lib/utils_base.h"
41#define DEFAULT_PORT "161" 41#include "../lib/output.h"
42#define DEFAULT_MIBLIST "ALL" 42#include "check_snmp.d/check_snmp_helpers.h"
43#define DEFAULT_PROTOCOL "1" 43
44#define DEFAULT_RETRIES 5 44#include <bits/getopt_core.h>
45#define DEFAULT_AUTH_PROTOCOL "MD5" 45#include <bits/getopt_ext.h>
46#define DEFAULT_PRIV_PROTOCOL "DES" 46#include <strings.h>
47#define DEFAULT_DELIMITER "=" 47#include <stdint.h>
48#define DEFAULT_OUTPUT_DELIMITER " " 48
49#define DEFAULT_BUFFER_SIZE 100 49#include "check_snmp.d/config.h"
50 50#include <stdlib.h>
51#define mark(a) ((a) != 0 ? "*" : "") 51#include <arpa/inet.h>
52 52#include <net-snmp/library/parse.h>
53#define CHECK_UNDEF 0 53#include <net-snmp/net-snmp-config.h>
54#define CRIT_PRESENT 1 54#include <net-snmp/net-snmp-includes.h>
55#define CRIT_STRING 2 55#include <net-snmp/library/snmp.h>
56#define CRIT_REGEX 4 56#include <net-snmp/library/keytools.h>
57#define WARN_PRESENT 8 57#include <net-snmp/library/snmp_api.h>
58 58#include <net-snmp/session_api.h>
59#define OID_COUNT_STEP 8 59#include <net-snmp/definitions.h>
60 60#include <net-snmp/library/asn1.h>
61/* Longopts only arguments */ 61#include <net-snmp/mib_api.h>
62#define L_CALCULATE_RATE CHAR_MAX + 1 62#include <net-snmp/library/snmp_impl.h>
63#define L_RATE_MULTIPLIER CHAR_MAX + 2 63#include <string.h>
64#define L_INVERT_SEARCH CHAR_MAX + 3 64#include "../gl/regex.h"
65#define L_OFFSET CHAR_MAX + 4 65#include "../gl/base64.h"
66#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 66#include <assert.h>
67 67
68/* Gobble to string - stop incrementing c when c[0] match one of the 68const char DEFAULT_COMMUNITY[] = "public";
69 * characters in s */ 69const char DEFAULT_MIBLIST[] = "ALL";
70#define GOBBLE_TOS(c, s) \ 70#define DEFAULT_AUTH_PROTOCOL "MD5"
71 while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ 71
72 c++; \ 72#ifdef HAVE_USM_DES_PRIV_PROTOCOL
73# define DEFAULT_PRIV_PROTOCOL "DES"
74#else
75# define DEFAULT_PRIV_PROTOCOL "AES"
76#endif
77
78typedef struct proces_arguments_wrapper {
79 int errorcode;
80 check_snmp_config config;
81} process_arguments_wrapper;
82
83static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
84static char *trim_whitespaces_and_check_quoting(char *str);
85static char *get_next_argument(char *str);
86void print_usage(void);
87void print_help(void);
88
89int verbose = 0;
90
91typedef struct {
92 int errorcode;
93 char *state_string;
94} gen_state_string_type;
95gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) {
96 char *encoded_string = NULL;
97 gen_state_string_type result = {.errorcode = OK, .state_string = NULL};
98
99 if (verbose > 1) {
100 printf("%s:\n", __FUNCTION__);
101 for (size_t i = 0; i < num_of_entries; i++) {
102 printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp));
103 switch (entries[i].type) {
104 case ASN_GAUGE:
105 printf("Type GAUGE\n");
106 break;
107 case ASN_TIMETICKS:
108 printf("Type TIMETICKS\n");
109 break;
110 case ASN_COUNTER:
111 printf("Type COUNTER\n");
112 break;
113 case ASN_UINTEGER:
114 printf("Type UINTEGER\n");
115 break;
116 case ASN_COUNTER64:
117 printf("Type COUNTER64\n");
118 break;
119 case ASN_FLOAT:
120 printf("Type FLOAT\n");
121 case ASN_DOUBLE:
122 printf("Type DOUBLE\n");
123 break;
124 case ASN_INTEGER:
125 printf("Type INTEGER\n");
126 break;
127 }
128
129 switch (entries[i].type) {
130 case ASN_GAUGE:
131 case ASN_TIMETICKS:
132 case ASN_COUNTER:
133 case ASN_UINTEGER:
134 case ASN_COUNTER64:
135 printf("Value %llu\n", entries[i].value.uIntVal);
136 break;
137 case ASN_FLOAT:
138 case ASN_DOUBLE:
139 printf("Value %f\n", entries[i].value.doubleVal);
140 break;
141 case ASN_INTEGER:
142 printf("Value %lld\n", entries[i].value.intVal);
143 break;
144 }
145 }
146 }
147
148 idx_t encoded = base64_encode_alloc((const char *)entries,
149 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
150 &encoded_string);
151
152 if (encoded > 0 && encoded_string != NULL) {
153 // success
154 if (verbose > 1) {
155 printf("encoded string: %s\n", encoded_string);
156 printf("encoded string length: %lu\n", strlen(encoded_string));
157 }
158 result.state_string = encoded_string;
159 return result;
73 } 160 }
74/* Given c, keep track of backslashes (bk) and double-quotes (dq) 161 result.errorcode = ERROR;
75 * from c[0] */ 162 return result;
76#define COUNT_SEQ(c, bk, dq) \ 163}
77 switch (c[0]) { \ 164
78 case '\\': \ 165typedef struct {
79 if (bk) \ 166 int errorcode;
80 bk--; \ 167 check_snmp_state_entry *state;
81 else \ 168} recover_state_data_type;
82 bk++; \ 169recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
83 break; \ 170 recover_state_data_type result = {.errorcode = OK, .state = NULL};
84 case '"': \ 171
85 if (!dq) { \ 172 if (verbose > 1) {
86 dq++; \ 173 printf("%s:\n", __FUNCTION__);
87 } else if (!bk) { \ 174 printf("State string: %s\n", state_string);
88 dq--; \ 175 printf("State string length: %lu\n", state_string_length);
89 } else { \
90 bk--; \
91 } \
92 break; \
93 } 176 }
94 177
95static int process_arguments(int, char **); 178 idx_t outlen = 0;
96static int validate_arguments(void); 179 bool decoded =
97static char *thisarg(char *str); 180 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
98static char *nextarg(char *str); 181
99void print_usage(void); 182 if (!decoded) {
100static void print_help(void); 183 if (verbose) {
101static char *multiply(char *str); 184 printf("Failed to decode state string\n");
102 185 }
103#include "regex.h" 186 // failure to decode
104static char regex_expect[MAX_INPUT_BUFFER] = ""; 187 result.errorcode = ERROR;
105static regex_t preg; 188 return result;
106static regmatch_t pmatch[10]; 189 }
107static char errbuf[MAX_INPUT_BUFFER] = ""; 190
108static char perfstr[MAX_INPUT_BUFFER] = "| "; 191 if (result.state == NULL) {
109static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 192 // Memory Error?
110static int eflags = 0; 193 result.errorcode = ERROR;
111static int errcode, excode; 194 return result;
112 195 }
113static char *server_address = NULL; 196
114static char *community = NULL; 197 if (verbose > 1) {
115static char **contextargs = NULL; 198 printf("Recovered %lu entries of size %lu\n",
116static char *context = NULL; 199 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
117static char **authpriv = NULL; 200
118static char *proto = NULL; 201 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
119static char *seclevel = NULL; 202 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
120static char *secname = NULL; 203 ctime(&result.state[i].timestamp));
121static char *authproto = NULL; 204 switch (result.state[i].type) {
122static char *privproto = NULL; 205 case ASN_GAUGE:
123static char *authpasswd = NULL; 206 printf("Type GAUGE\n");
124static char *privpasswd = NULL; 207 break;
125static int nulloid = STATE_UNKNOWN; 208 case ASN_TIMETICKS:
126static char **oids = NULL; 209 printf("Type TIMETICKS\n");
127static size_t oids_size = 0; 210 break;
128static char *label; 211 case ASN_COUNTER:
129static char *units; 212 printf("Type COUNTER\n");
130static char *port; 213 break;
131static char *snmpcmd; 214 case ASN_UINTEGER:
132static char string_value[MAX_INPUT_BUFFER] = ""; 215 printf("Type UINTEGER\n");
133static int invert_search = 0; 216 break;
134static char **labels = NULL; 217 case ASN_COUNTER64:
135static char **unitv = NULL; 218 printf("Type COUNTER64\n");
136static size_t nlabels = 0; 219 break;
137static size_t labels_size = OID_COUNT_STEP; 220 case ASN_FLOAT:
138static size_t nunits = 0; 221 printf("Type FLOAT\n");
139static size_t unitv_size = OID_COUNT_STEP; 222 case ASN_DOUBLE:
140static size_t numoids = 0; 223 printf("Type DOUBLE\n");
141static int numauthpriv = 0; 224 break;
142static int numcontext = 0; 225 case ASN_INTEGER:
143static int verbose = 0; 226 printf("Type INTEGER\n");
144static bool usesnmpgetnext = false; 227 break;
145static char *warning_thresholds = NULL; 228 }
146static char *critical_thresholds = NULL; 229
147static thresholds **thlds; 230 switch (result.state[i].type) {
148static size_t thlds_size = OID_COUNT_STEP; 231 case ASN_GAUGE:
149static double *response_value; 232 case ASN_TIMETICKS:
150static size_t response_size = OID_COUNT_STEP; 233 case ASN_COUNTER:
151static int retries = 0; 234 case ASN_UINTEGER:
152static int *eval_method; 235 case ASN_COUNTER64:
153static size_t eval_size = OID_COUNT_STEP; 236 printf("Value %llu\n", result.state[i].value.uIntVal);
154static char *delimiter; 237 break;
155static char *output_delim; 238 case ASN_FLOAT:
156static char *miblist = NULL; 239 case ASN_DOUBLE:
157static bool needmibs = false; 240 printf("Value %f\n", result.state[i].value.doubleVal);
158static int calculate_rate = 0; 241 break;
159static double offset = 0.0; 242 case ASN_INTEGER:
160static int rate_multiplier = 1; 243 printf("Value %lld\n", result.state[i].value.intVal);
161static state_data *previous_state; 244 break;
162static double *previous_value; 245 }
163static size_t previous_size = OID_COUNT_STEP; 246 }
164static int perf_labels = 1; 247 }
165static char *ip_version = ""; 248
166static double multiplier = 1.0; 249 return result;
167static char *fmtstr = "";
168static bool fmtstr_set = false;
169static char buffer[DEFAULT_BUFFER_SIZE];
170static bool ignore_mib_parsing_errors = false;
171
172static char *fix_snmp_range(char *th) {
173 double left;
174 double right;
175 char *colon;
176 char *ret;
177
178 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
179 return th;
180
181 left = strtod(th, NULL);
182 right = strtod(colon + 1, NULL);
183 if (right >= left)
184 return th;
185
186 if ((ret = malloc(strlen(th) + 2)) == NULL)
187 die(STATE_UNKNOWN, _("Cannot malloc"));
188 *colon = '\0';
189 sprintf(ret, "@%s:%s", colon + 1, th);
190 free(th);
191 return ret;
192} 250}
193 251
194int main(int argc, char **argv) { 252int main(int argc, char **argv) {
195 int len;
196 int total_oids;
197 size_t line;
198 unsigned int bk_count = 0;
199 unsigned int dq_count = 0;
200 int iresult = STATE_UNKNOWN;
201 int result = STATE_UNKNOWN;
202 int return_code = 0;
203 int external_error = 0;
204 char **command_line = NULL;
205 char *cl_hidden_auth = NULL;
206 char *oidname = NULL;
207 char *response = NULL;
208 char *mult_resp = NULL;
209 char *outbuff;
210 char *ptr = NULL;
211 char *show = NULL;
212 char *th_warn = NULL;
213 char *th_crit = NULL;
214 char type[8] = "";
215 output chld_out;
216 output chld_err;
217 char *previous_string = NULL;
218 char *ap = NULL;
219 char *state_string = NULL;
220 size_t response_length;
221 size_t current_length;
222 size_t string_length;
223 char *temp_string = NULL;
224 char *quote_string = NULL;
225 time_t current_time;
226 double temp_double;
227 time_t duration;
228 char *conv = "12345678";
229 int is_counter = 0;
230
231 setlocale(LC_ALL, ""); 253 setlocale(LC_ALL, "");
232 bindtextdomain(PACKAGE, LOCALEDIR); 254 bindtextdomain(PACKAGE, LOCALEDIR);
233 textdomain(PACKAGE); 255 textdomain(PACKAGE);
234 256
235 labels = malloc(labels_size * sizeof(*labels));
236 unitv = malloc(unitv_size * sizeof(*unitv));
237 thlds = malloc(thlds_size * sizeof(*thlds));
238 response_value = malloc(response_size * sizeof(*response_value));
239 previous_value = malloc(previous_size * sizeof(*previous_value));
240 eval_method = calloc(eval_size, sizeof(*eval_method));
241 oids = calloc(oids_size, sizeof(char *));
242
243 label = strdup("SNMP");
244 units = strdup("");
245 port = strdup(DEFAULT_PORT);
246 outbuff = strdup("");
247 delimiter = strdup(" = ");
248 output_delim = strdup(DEFAULT_OUTPUT_DELIMITER);
249 timeout_interval = DEFAULT_SOCKET_TIMEOUT; 257 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
250 retries = DEFAULT_RETRIES;
251 258
252 np_init((char *)progname, argc, argv); 259 np_init((char *)progname, argc, argv);
253 260
261 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
262
254 /* Parse extra opts if any */ 263 /* Parse extra opts if any */
255 argv = np_extra_opts(&argc, argv, progname); 264 argv = np_extra_opts(&argc, argv, progname);
256 265
257 np_set_args(argc, argv); 266 np_set_args(argc, argv);
258 267
259 time(&current_time); 268 // Initialize net-snmp before touching the session we are going to use
269 init_snmp("check_snmp");
260 270
261 if (process_arguments(argc, argv) == ERROR) 271 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
272 if (paw_tmp.errorcode == ERROR) {
262 usage4(_("Could not parse arguments")); 273 usage4(_("Could not parse arguments"));
263
264 if (calculate_rate) {
265 if (!strcmp(label, "SNMP"))
266 label = strdup("SNMP RATE");
267
268 size_t i = 0;
269
270 previous_state = np_state_read();
271 if (previous_state != NULL) {
272 /* Split colon separated values */
273 previous_string = strdup((char *)previous_state->data);
274 while ((ap = strsep(&previous_string, ":")) != NULL) {
275 if (verbose > 2)
276 printf("State for %zd=%s\n", i, ap);
277 while (i >= previous_size) {
278 previous_size += OID_COUNT_STEP;
279 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
280 }
281 previous_value[i++] = strtod(ap, NULL);
282 }
283 }
284 } 274 }
285 275
286 /* Populate the thresholds */ 276 check_snmp_config config = paw_tmp.config;
287 th_warn = warning_thresholds;
288 th_crit = critical_thresholds;
289 for (size_t i = 0; i < numoids; i++) {
290 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
291 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
292 /* translate "2:1" to "@1:2" for backwards compatibility */
293 w = w ? fix_snmp_range(w) : NULL;
294 c = c ? fix_snmp_range(c) : NULL;
295
296 while (i >= thlds_size) {
297 thlds_size += OID_COUNT_STEP;
298 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
299 }
300
301 /* Skip empty thresholds, while avoiding segfault */
302 set_thresholds(&thlds[i], w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
303 if (w) {
304 th_warn = strchr(th_warn, ',');
305 if (th_warn)
306 th_warn++;
307 free(w);
308 }
309 if (c) {
310 th_crit = strchr(th_crit, ',');
311 if (th_crit)
312 th_crit++;
313 free(c);
314 }
315 }
316 277
317 /* Create the command array to execute */ 278 if (config.output_format_is_set) {
318 if (usesnmpgetnext) { 279 mp_set_format(config.output_format);
319 snmpcmd = strdup(PATH_TO_SNMPGETNEXT);
320 } else {
321 snmpcmd = strdup(PATH_TO_SNMPGET);
322 } 280 }
323 281
324 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ 282 /* Set signal handling and alarm */
325 283 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
326 unsigned index = 0; 284 usage4(_("Cannot catch SIGALRM"));
327 command_line = calloc(11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof(char *));
328
329 command_line[index++] = snmpcmd;
330 command_line[index++] = strdup("-Le");
331 command_line[index++] = strdup("-t");
332 xasprintf(&command_line[index++], "%d", timeout_interval);
333 command_line[index++] = strdup("-r");
334 xasprintf(&command_line[index++], "%d", retries);
335 command_line[index++] = strdup("-m");
336 command_line[index++] = strdup(miblist);
337 command_line[index++] = "-v";
338 command_line[index++] = strdup(proto);
339
340 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''",
341 proto);
342
343 if (ignore_mib_parsing_errors) {
344 command_line[index++] = "-Pe";
345 xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth);
346 } 285 }
347 286
348 for (int i = 0; i < numcontext; i++) { 287 time_t current_time;
349 command_line[index++] = contextargs[i]; 288 time(&current_time);
350 }
351 289
352 for (int i = 0; i < numauthpriv; i++) { 290 if (verbose > 2) {
353 command_line[index++] = authpriv[i]; 291 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
354 } 292 }
355 293
356 xasprintf(&command_line[index++], "%s:%s", server_address, port); 294 snmp_responces response = do_snmp_query(config.snmp_params);
357 295
358 xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); 296 mp_check overall = mp_check_init();
359 297
360 for (size_t i = 0; i < numoids; i++) { 298 if (response.errorcode == OK) {
361 command_line[index++] = oids[i]; 299 mp_subcheck sc_successfull_query = mp_subcheck_init();
362 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); 300 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
301 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
302 mp_add_subcheck_to_check(&overall, sc_successfull_query);
303 } else {
304 // Error treatment here, either partial or whole
305 mp_subcheck sc_failed_query = mp_subcheck_init();
306 xasprintf(&sc_failed_query.output, "SNMP query failed");
307 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
308 mp_add_subcheck_to_check(&overall, sc_failed_query);
309 mp_exit(overall);
363 } 310 }
364 311
365 command_line[index++] = NULL; 312 check_snmp_state_entry *prev_state = NULL;
313 bool have_previous_state = false;
366 314
367 if (verbose) { 315 if (config.evaluation_params.calculate_rate) {
368 printf("%s\n", cl_hidden_auth); 316 state_data *previous_state = np_state_read(stateKey);
369 } 317 if (previous_state == NULL) {
318 // failed to recover state
319 // or no previous state
320 have_previous_state = false;
321 } else {
322 // sanity check
323 recover_state_data_type prev_state_wrapper =
324 recover_state_data(previous_state->data, (idx_t)previous_state->length);
370 325
371 /* Set signal handling and alarm */ 326 if (prev_state_wrapper.errorcode == OK) {
372 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 327 have_previous_state = true;
373 usage4(_("Cannot catch SIGALRM")); 328 prev_state = prev_state_wrapper.state;
374 } 329 } else {
375 alarm(timeout_interval * retries + 5); 330 have_previous_state = false;
376 331 prev_state = NULL;
377 /* Run the command */
378 return_code = cmd_run_array(command_line, &chld_out, &chld_err, 0);
379
380 /* disable alarm again */
381 alarm(0);
382
383 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
384 only return state unknown if return code is non zero or there is no stdout.
385 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
386 */
387 if (return_code != 0)
388 external_error = 1;
389 if (chld_out.lines == 0)
390 external_error = 1;
391 if (external_error) {
392 if (chld_err.lines > 0) {
393 printf(_("External command error: %s\n"), chld_err.line[0]);
394 for (size_t i = 1; i < chld_err.lines; i++) {
395 printf("%s\n", chld_err.line[i]);
396 } 332 }
397 } else {
398 printf(_("External command error with no output (return code: %d)\n"), return_code);
399 } 333 }
400 exit(STATE_UNKNOWN);
401 } 334 }
402 335
403 if (verbose) { 336 check_snmp_state_entry *new_state = NULL;
404 for (size_t i = 0; i < chld_out.lines; i++) { 337 if (config.evaluation_params.calculate_rate) {
405 printf("%s\n", chld_out.line[i]); 338 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
339 if (new_state == NULL) {
340 die(STATE_UNKNOWN, "memory allocation failed");
406 } 341 }
407 } 342 }
408 343
409 line = 0; 344 // We got the the query results, now process them
410 total_oids = 0; 345 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
411 for (size_t i = 0; line < chld_out.lines && i < numoids; line++, i++, total_oids++) { 346 if (verbose > 0) {
412 if (calculate_rate) 347 printf("loop_index: %zu\n", loop_index);
413 conv = "%.10g";
414 else
415 conv = "%.0f";
416
417 ptr = chld_out.line[line];
418 oidname = strpcpy(oidname, ptr, delimiter);
419 response = strstr(ptr, delimiter);
420 if (response == NULL)
421 break;
422
423 if (verbose > 2) {
424 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i + 1, line + 1, oidname, response);
425 } 348 }
426 349
427 /* Clean up type array - Sol10 does not necessarily zero it out */ 350 check_snmp_state_entry previous_unit_state = {};
428 bzero(type, sizeof(type)); 351 if (config.evaluation_params.calculate_rate && have_previous_state) {
429 352 previous_unit_state = prev_state[loop_index];
430 is_counter = 0; 353 }
431 /* We strip out the datatype indicator for PHBs */
432 if (strstr(response, "Gauge: ")) {
433 show = multiply(strstr(response, "Gauge: ") + 7);
434 } else if (strstr(response, "Gauge32: ")) {
435 show = multiply(strstr(response, "Gauge32: ") + 9);
436 } else if (strstr(response, "Counter32: ")) {
437 show = strstr(response, "Counter32: ") + 11;
438 is_counter = 1;
439 if (!calculate_rate)
440 strcpy(type, "c");
441 } else if (strstr(response, "Counter64: ")) {
442 show = strstr(response, "Counter64: ") + 11;
443 is_counter = 1;
444 if (!calculate_rate)
445 strcpy(type, "c");
446 } else if (strstr(response, "INTEGER: ")) {
447 show = multiply(strstr(response, "INTEGER: ") + 9);
448
449 if (fmtstr_set) {
450 conv = fmtstr;
451 }
452 } else if (strstr(response, "OID: ")) {
453 show = strstr(response, "OID: ") + 5;
454 } else if (strstr(response, "STRING: ")) {
455 show = strstr(response, "STRING: ") + 8;
456 conv = "%.10g";
457
458 /* Get the rest of the string on multi-line strings */
459 ptr = show;
460 COUNT_SEQ(ptr, bk_count, dq_count)
461 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
462 ptr++;
463 GOBBLE_TOS(ptr, "\n\"\\")
464 COUNT_SEQ(ptr, bk_count, dq_count)
465 }
466
467 if (dq_count) { /* unfinished line */
468 /* copy show verbatim first */
469 if (!mult_resp)
470 mult_resp = strdup("");
471 xasprintf(&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
472 /* then strip out unmatched double-quote from single-line output */
473 if (show[0] == '"')
474 show++;
475
476 /* Keep reading until we match end of double-quoted string */
477 for (line++; line < chld_out.lines; line++) {
478 ptr = chld_out.line[line];
479 xasprintf(&mult_resp, "%s%s\n", mult_resp, ptr);
480
481 COUNT_SEQ(ptr, bk_count, dq_count)
482 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
483 ptr++;
484 GOBBLE_TOS(ptr, "\n\"\\")
485 COUNT_SEQ(ptr, bk_count, dq_count)
486 }
487 /* Break for loop before next line increment when done */
488 if (!dq_count)
489 break;
490 }
491 }
492
493 } else if (strstr(response, "Timeticks: ")) {
494 show = strstr(response, "Timeticks: ");
495 } else
496 show = response + 3;
497 354
498 iresult = STATE_DEPENDENT; 355 check_snmp_evaluation single_eval =
356 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
357 config.snmp_params.test_units[loop_index], current_time,
358 previous_unit_state, have_previous_state);
499 359
500 /* Process this block for numeric comparisons */ 360 if (config.evaluation_params.calculate_rate &&
501 /* Make some special values,like Timeticks numeric only if a threshold is defined */ 361 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
502 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { 362 new_state[loop_index] = single_eval.state;
503 if (verbose > 2) {
504 print_thresholds(" thresholds", thlds[i]);
505 }
506 ptr = strpbrk(show, "-0123456789");
507 if (ptr == NULL) {
508 if (nulloid == 3)
509 die(STATE_UNKNOWN, _("No valid data returned (%s)\n"), show);
510 else if (nulloid == 0)
511 die(STATE_OK, _("No valid data returned (%s)\n"), show);
512 else if (nulloid == 1)
513 die(STATE_WARNING, _("No valid data returned (%s)\n"), show);
514 else if (nulloid == 2)
515 die(STATE_CRITICAL, _("No valid data returned (%s)\n"), show);
516 }
517 while (i >= response_size) {
518 response_size += OID_COUNT_STEP;
519 response_value = realloc(response_value, response_size * sizeof(*response_value));
520 }
521 response_value[i] = strtod(ptr, NULL) + offset;
522
523 if (calculate_rate) {
524 if (previous_state != NULL) {
525 duration = current_time - previous_state->time;
526 if (duration <= 0)
527 die(STATE_UNKNOWN, _("Time duration between plugin calls is invalid"));
528 temp_double = response_value[i] - previous_value[i];
529 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
530 if (is_counter) {
531 if (temp_double < (double)0.0)
532 temp_double += (double)4294967296.0; /* 2^32 */
533 if (temp_double < (double)0.0)
534 temp_double += (double)18446744069414584320.0; /* 2^64-2^32 */
535 ;
536 }
537 /* Convert to per second, then use multiplier */
538 temp_double = temp_double / duration * rate_multiplier;
539 iresult = get_status(temp_double, thlds[i]);
540 xasprintf(&show, conv, temp_double);
541 }
542 } else {
543 iresult = get_status(response_value[i], thlds[i]);
544 xasprintf(&show, conv, response_value[i]);
545 }
546 } 363 }
547 364
548 /* Process this block for string matching */ 365 mp_add_subcheck_to_check(&overall, single_eval.sc);
549 else if (eval_size > i && eval_method[i] & CRIT_STRING) { 366 }
550 if (strcmp(show, string_value))
551 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
552 else
553 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
554 }
555 367
556 /* Process this block for regex matching */ 368 if (config.evaluation_params.calculate_rate) {
557 else if (eval_size > i && eval_method[i] & CRIT_REGEX) { 369 // store state
558 excode = regexec(&preg, response, 10, pmatch, eflags); 370 gen_state_string_type current_state_wrapper =
559 if (excode == 0) { 371 gen_state_string(new_state, config.snmp_params.num_of_test_units);
560 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
561 } else if (excode != REG_NOMATCH) {
562 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
563 printf(_("Execute Error: %s\n"), errbuf);
564 exit(STATE_CRITICAL);
565 } else {
566 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
567 }
568 }
569 372
570 /* Process this block for existence-nonexistence checks */ 373 if (current_state_wrapper.errorcode == OK) {
571 /* TV: Should this be outside of this else block? */ 374 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
572 else { 375 } else {
573 if (eval_size > i && eval_method[i] & CRIT_PRESENT) 376 die(STATE_UNKNOWN, "failed to create state string");
574 iresult = STATE_CRITICAL;
575 else if (eval_size > i && eval_method[i] & WARN_PRESENT)
576 iresult = STATE_WARNING;
577 else if (response && iresult == STATE_DEPENDENT)
578 iresult = STATE_OK;
579 } 377 }
378 }
379 mp_exit(overall);
380}
580 381
581 /* Result is the worst outcome of all the OIDs tested */ 382/* process command-line arguments */
582 result = max_state(result, iresult); 383static process_arguments_wrapper process_arguments(int argc, char **argv) {
583 384 enum {
584 /* Prepend a label for this OID if there is one */ 385 /* Longopts only arguments */
585 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 386 invert_search_index = CHAR_MAX + 1,
586 xasprintf(&outbuff, "%s%s%s %s%s%s", outbuff, (i == 0) ? " " : output_delim, labels[i], mark(iresult), show, mark(iresult)); 387 offset_index,
587 else 388 ignore_mib_parsing_errors_index,
588 xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); 389 connection_prefix_index,
589 390 output_format_index,
590 /* Append a unit string for this OID if there is one */ 391 calculate_rate,
591 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) 392 rate_multiplier
592 xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); 393 };
593 394
594 /* Write perfdata with whatever can be parsed by strtod, if possible */ 395 static struct option longopts[] = {
595 ptr = NULL; 396 STD_LONG_OPTS,
596 strtod(show, &ptr); 397 {"community", required_argument, 0, 'C'},
597 if (ptr > show) { 398 {"oid", required_argument, 0, 'o'},
598 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 399 {"object", required_argument, 0, 'o'},
599 temp_string = labels[i]; 400 {"delimiter", required_argument, 0, 'd'},
600 else 401 {"nulloid", required_argument, 0, 'z'},
601 temp_string = oidname; 402 {"output-delimiter", required_argument, 0, 'D'},
602 if (strpbrk(temp_string, " ='\"") == NULL) { 403 {"string", required_argument, 0, 's'},
603 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 404 {"timeout", required_argument, 0, 't'},
604 } else { 405 {"regex", required_argument, 0, 'r'},
605 if (strpbrk(temp_string, "'") == NULL) { 406 {"ereg", required_argument, 0, 'r'},
606 quote_string = "'"; 407 {"eregi", required_argument, 0, 'R'},
607 } else { 408 {"label", required_argument, 0, 'l'},
608 quote_string = "\""; 409 {"units", required_argument, 0, 'u'},
609 } 410 {"port", required_argument, 0, 'p'},
610 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 411 {"retries", required_argument, 0, 'e'},
611 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 412 {"miblist", required_argument, 0, 'm'},
612 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 413 {"protocol", required_argument, 0, 'P'},
613 } 414 {"context", required_argument, 0, 'N'},
614 strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); 415 {"seclevel", required_argument, 0, 'L'},
615 len = sizeof(perfstr) - strlen(perfstr) - 1; 416 {"secname", required_argument, 0, 'U'},
616 strncat(perfstr, show, len > ptr - show ? ptr - show : len); 417 {"authproto", required_argument, 0, 'a'},
418 {"privproto", required_argument, 0, 'x'},
419 {"authpasswd", required_argument, 0, 'A'},
420 {"privpasswd", required_argument, 0, 'X'},
421 {"next", no_argument, 0, 'n'},
422 {"offset", required_argument, 0, offset_index},
423 {"invert-search", no_argument, 0, invert_search_index},
424 {"perf-oids", no_argument, 0, 'O'},
425 {"ipv4", no_argument, 0, '4'},
426 {"ipv6", no_argument, 0, '6'},
427 {"multiplier", required_argument, 0, 'M'},
428 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
429 {"connection-prefix", required_argument, 0, connection_prefix_index},
430 {"output-format", required_argument, 0, output_format_index},
431 {"rate", no_argument, 0, calculate_rate},
432 {"rate-multiplier", required_argument, 0, rate_multiplier},
433 {0, 0, 0, 0}};
434
435 if (argc < 2) {
436 process_arguments_wrapper result = {
437 .errorcode = ERROR,
438 };
439 return result;
440 }
617 441
618 if (strcmp(type, "") != 0) { 442 // Count number of OIDs here first
619 strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); 443 int option = 0;
620 } 444 size_t oid_counter = 0;
445 while (true) {
446 int option_char = getopt_long(
447 argc, argv,
448 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
621 449
622 if (warning_thresholds) { 450 if (option_char == -1 || option_char == EOF) {
623 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 451 break;
624 if (thlds[i]->warning && thlds[i]->warning->text) 452 }
625 strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1);
626 }
627 453
628 if (critical_thresholds) { 454 switch (option_char) {
629 if (!warning_thresholds) 455 case 'o': {
630 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 456 // we are going to parse this again, so we work on a copy of that string
631 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 457 char *tmp_oids = strdup(optarg);
632 if (thlds[i]->critical && thlds[i]->critical->text) 458 if (tmp_oids == NULL) {
633 strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); 459 die(STATE_UNKNOWN, "strdup failed");
634 } 460 }
635 461
636 strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); 462 for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL;
637 } 463 ptr = strtok(NULL, ", "), oid_counter++) {
638 }
639
640 /* Save state data, as all data collected now */
641 if (calculate_rate) {
642 string_length = 1024;
643 state_string = malloc(string_length);
644 if (state_string == NULL)
645 die(STATE_UNKNOWN, _("Cannot malloc"));
646
647 current_length = 0;
648 for (int i = 0; i < total_oids; i++) {
649 xasprintf(&temp_string, "%.0f", response_value[i]);
650 if (temp_string == NULL)
651 die(STATE_UNKNOWN, _("Cannot asprintf()"));
652 response_length = strlen(temp_string);
653 if (current_length + response_length > string_length) {
654 string_length = current_length + 1024;
655 state_string = realloc(state_string, string_length);
656 if (state_string == NULL)
657 die(STATE_UNKNOWN, _("Cannot realloc()"));
658 } 464 }
659 strcpy(&state_string[current_length], temp_string); 465 break;
660 current_length = current_length + response_length;
661 state_string[current_length] = ':';
662 current_length++;
663 free(temp_string);
664 } 466 }
665 state_string[--current_length] = '\0'; 467 case '?': /* usage */
666 if (verbose > 2) 468 usage5();
667 printf("State string=%s\n", state_string); 469 // fallthrough
470 case 'h': /* help */
471 print_help();
472 exit(STATE_UNKNOWN);
473 case 'V': /* version */
474 print_revision(progname, NP_VERSION);
475 exit(STATE_UNKNOWN);
668 476
669 /* This is not strictly the same as time now, but any subtle variations will cancel out */ 477 default:
670 np_state_write_string(current_time, state_string); 478 continue;
671 if (previous_state == NULL) {
672 /* Or should this be highest state? */
673 die(STATE_OK, _("No previous data to calculate rate - assume okay"));
674 } 479 }
675 } 480 }
676 481
677 printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); 482 /* Check whether at least one OID was given */
678 if (mult_resp) 483 if (oid_counter == 0) {
679 printf("%s", mult_resp); 484 die(STATE_UNKNOWN, _("No OIDs specified\n"));
485 }
680 486
681 return result; 487 // Allocate space for test units
682} 488 check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit));
489 if (tmp == NULL) {
490 die(STATE_UNKNOWN, "Failed to calloc");
491 }
683 492
684/* process command-line arguments */ 493 for (size_t i = 0; i < oid_counter; i++) {
685int process_arguments(int argc, char **argv) { 494 tmp[i] = check_snmp_test_unit_init();
686 static struct option longopts[] = {STD_LONG_OPTS,
687 {"community", required_argument, 0, 'C'},
688 {"oid", required_argument, 0, 'o'},
689 {"object", required_argument, 0, 'o'},
690 {"delimiter", required_argument, 0, 'd'},
691 {"nulloid", required_argument, 0, 'z'},
692 {"output-delimiter", required_argument, 0, 'D'},
693 {"string", required_argument, 0, 's'},
694 {"timeout", required_argument, 0, 't'},
695 {"regex", required_argument, 0, 'r'},
696 {"ereg", required_argument, 0, 'r'},
697 {"eregi", required_argument, 0, 'R'},
698 {"label", required_argument, 0, 'l'},
699 {"units", required_argument, 0, 'u'},
700 {"port", required_argument, 0, 'p'},
701 {"retries", required_argument, 0, 'e'},
702 {"miblist", required_argument, 0, 'm'},
703 {"protocol", required_argument, 0, 'P'},
704 {"context", required_argument, 0, 'N'},
705 {"seclevel", required_argument, 0, 'L'},
706 {"secname", required_argument, 0, 'U'},
707 {"authproto", required_argument, 0, 'a'},
708 {"privproto", required_argument, 0, 'x'},
709 {"authpasswd", required_argument, 0, 'A'},
710 {"privpasswd", required_argument, 0, 'X'},
711 {"next", no_argument, 0, 'n'},
712 {"rate", no_argument, 0, L_CALCULATE_RATE},
713 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
714 {"offset", required_argument, 0, L_OFFSET},
715 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
716 {"perf-oids", no_argument, 0, 'O'},
717 {"ipv4", no_argument, 0, '4'},
718 {"ipv6", no_argument, 0, '6'},
719 {"multiplier", required_argument, 0, 'M'},
720 {"fmtstr", required_argument, 0, 'f'},
721 {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS},
722 {0, 0, 0, 0}};
723
724 if (argc < 2)
725 return ERROR;
726
727 /* reverse compatibility for very old non-POSIX usage forms */
728 for (int c = 1; c < argc; c++) {
729 if (strcmp("-to", argv[c]) == 0)
730 strcpy(argv[c], "-t");
731 if (strcmp("-wv", argv[c]) == 0)
732 strcpy(argv[c], "-w");
733 if (strcmp("-cv", argv[c]) == 0)
734 strcpy(argv[c], "-c");
735 } 495 }
736 496
737 size_t j = 0; 497 check_snmp_config config = check_snmp_config_init();
738 size_t jj = 0; 498 config.snmp_params.test_units = tmp;
499 config.snmp_params.num_of_test_units = oid_counter;
500
501 option = 0;
502 optind = 1; // Reset argument scanner
503 size_t tmp_oid_counter = 0;
504 size_t eval_counter = 0;
505 size_t unitv_counter = 0;
506 size_t labels_counter = 0;
507 unsigned char *authpasswd = NULL;
508 unsigned char *privpasswd = NULL;
509 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
510 char *port = NULL;
511 char *miblist = NULL;
512 char *connection_prefix = NULL;
513 bool snmp_version_set_explicitely = false;
514 // TODO error checking
739 while (true) { 515 while (true) {
740 int option = 0; 516 int option_char = getopt_long(
741 int option_char = getopt_long(argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); 517 argc, argv,
518 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
742 519
743 if (option_char == -1 || option_char == EOF) 520 if (option_char == -1 || option_char == EOF) {
744 break; 521 break;
522 }
745 523
746 switch (option_char) { 524 switch (option_char) {
747 case '?': /* usage */ 525 case '?': /* usage */
@@ -758,64 +536,155 @@ int process_arguments(int argc, char **argv) {
758 536
759 /* Connection info */ 537 /* Connection info */
760 case 'C': /* group or community */ 538 case 'C': /* group or community */
761 community = optarg; 539 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
540 config.snmp_params.snmp_session.community_len = strlen(optarg);
762 break; 541 break;
763 case 'H': /* Host or server */ 542 case 'H': /* Host or server */
764 server_address = optarg; 543 config.snmp_params.snmp_session.peername = optarg;
765 break; 544 break;
766 case 'p': /* TCP port number */ 545 case 'p': /*port number */
546 // Add port to "peername" below to not rely on argument order
767 port = optarg; 547 port = optarg;
768 break; 548 break;
769 case 'm': /* List of MIBS */ 549 case 'm': /* List of MIBS */
770 miblist = optarg; 550 miblist = optarg;
771 break; 551 break;
772 case 'n': /* usesnmpgetnext */ 552 case 'n': /* use_getnext instead of get */
773 usesnmpgetnext = true; 553 config.snmp_params.use_getnext = true;
774 break; 554 break;
775 case 'P': /* SNMP protocol version */ 555 case 'P': /* SNMP protocol version */
776 proto = optarg; 556 if (strcasecmp("1", optarg) == 0) {
557 config.snmp_params.snmp_session.version = SNMP_VERSION_1;
558 } else if (strcasecmp("2c", optarg) == 0) {
559 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
560 } else if (strcasecmp("3", optarg) == 0) {
561 config.snmp_params.snmp_session.version = SNMP_VERSION_3;
562 } else {
563 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg);
564 }
565 snmp_version_set_explicitely = true;
566
777 break; 567 break;
778 case 'N': /* SNMPv3 context */ 568 case 'N': /* SNMPv3 context name */
779 context = optarg; 569 config.snmp_params.snmp_session.contextName = optarg;
570 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
780 break; 571 break;
781 case 'L': /* security level */ 572 case 'L': /* security level */
782 seclevel = optarg; 573 if (strcasecmp("noAuthNoPriv", optarg) == 0) {
574 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
575 } else if (strcasecmp("authNoPriv", optarg) == 0) {
576 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
577 } else if (strcasecmp("authPriv", optarg) == 0) {
578 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
579 } else {
580 die(STATE_UNKNOWN, "invalid security level: %s", optarg);
581 }
783 break; 582 break;
784 case 'U': /* security username */ 583 case 'U': /* security username */
785 secname = optarg; 584 config.snmp_params.snmp_session.securityName = optarg;
585 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
786 break; 586 break;
787 case 'a': /* auth protocol */ 587 case 'a': /* auth protocol */
788 authproto = optarg; 588 // SNMPv3: SHA or MD5
589 // TODO Test for availability of individual protocols
590 if (strcasecmp("MD5", optarg) == 0) {
591 config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol;
592 config.snmp_params.snmp_session.securityAuthProtoLen =
593 OID_LENGTH(usmHMACMD5AuthProtocol);
594 } else if (strcasecmp("SHA", optarg) == 0) {
595 config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol;
596 config.snmp_params.snmp_session.securityAuthProtoLen =
597 OID_LENGTH(usmHMACSHA1AuthProtocol);
598 } else if (strcasecmp("SHA224", optarg) == 0) {
599 config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
600 config.snmp_params.snmp_session.securityAuthProtoLen =
601 OID_LENGTH(usmHMAC128SHA224AuthProtocol);
602 } else if (strcasecmp("SHA256", optarg) == 0) {
603 config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
604 config.snmp_params.snmp_session.securityAuthProtoLen =
605 OID_LENGTH(usmHMAC192SHA256AuthProtocol);
606 } else if (strcasecmp("SHA384", optarg) == 0) {
607 config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
608 config.snmp_params.snmp_session.securityAuthProtoLen =
609 OID_LENGTH(usmHMAC256SHA384AuthProtocol);
610 } else if (strcasecmp("SHA512", optarg) == 0) {
611 config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
612 config.snmp_params.snmp_session.securityAuthProtoLen =
613 OID_LENGTH(usmHMAC384SHA512AuthProtocol);
614 } else {
615 die(STATE_UNKNOWN, "Unknown authentication protocol");
616 }
789 break; 617 break;
790 case 'x': /* priv protocol */ 618 case 'x': /* priv protocol */
791 privproto = optarg; 619 if (strcasecmp("DES", optarg) == 0) {
620#ifdef HAVE_USM_DES_PRIV_PROTOCOL
621 config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol;
622 config.snmp_params.snmp_session.securityAuthProtoLen =
623 OID_LENGTH(usmDESPrivProtocol);
624#else
625 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform");
626#endif
627 } else if (strcasecmp("AES", optarg) == 0) {
628 config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol;
629 config.snmp_params.snmp_session.securityAuthProtoLen =
630 OID_LENGTH(usmAESPrivProtocol);
631 // } else if (strcasecmp("AES128", optarg)) {
632 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol;
633 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol)
634 // / OID_LENGTH(oid);
635 } else if (strcasecmp("AES192", optarg) == 0) {
636 config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol;
637 config.snmp_params.snmp_session.securityAuthProtoLen =
638 OID_LENGTH(usmAES192PrivProtocol);
639 } else if (strcasecmp("AES256", optarg) == 0) {
640 config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol;
641 config.snmp_params.snmp_session.securityAuthProtoLen =
642 OID_LENGTH(usmAES256PrivProtocol);
643 // } else if (strcasecmp("AES192Cisco", optarg)) {
644 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol;
645 // config.snmp_session.securityAuthProtoLen =
646 // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if
647 // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto =
648 // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen =
649 // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if
650 // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto
651 // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
652 // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if
653 // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto
654 // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
655 // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid);
656 } else {
657 die(STATE_UNKNOWN, "Unknown privacy protocol");
658 }
792 break; 659 break;
793 case 'A': /* auth passwd */ 660 case 'A': /* auth passwd */
794 authpasswd = optarg; 661 authpasswd = (unsigned char *)optarg;
795 break; 662 break;
796 case 'X': /* priv passwd */ 663 case 'X': /* priv passwd */
797 privpasswd = optarg; 664 privpasswd = (unsigned char *)optarg;
665 break;
666 case 'e':
667 case 'E':
668 if (!is_integer(optarg)) {
669 usage2(_("Retries interval must be a positive integer"), optarg);
670 } else {
671 config.snmp_params.snmp_session.retries = atoi(optarg);
672 }
798 break; 673 break;
799 case 't': /* timeout period */ 674 case 't': /* timeout period */
800 if (!is_integer(optarg)) 675 if (!is_integer(optarg)) {
801 usage2(_("Timeout interval must be a positive integer"), optarg); 676 usage2(_("Timeout interval must be a positive integer"), optarg);
802 else 677 } else {
803 timeout_interval = atoi(optarg); 678 timeout_interval = (unsigned int)atoi(optarg);
679 }
804 break; 680 break;
805 681
806 /* Test parameters */ 682 /* Test parameters */
807 case 'c': /* critical threshold */ 683 case 'c': /* critical threshold */
808 critical_thresholds = optarg; 684 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
809 break; 685 break;
810 case 'w': /* warning threshold */ 686 case 'w': /* warning threshold */
811 warning_thresholds = optarg; 687 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
812 break;
813 case 'e': /* PRELIMINARY - may change */
814 case 'E': /* PRELIMINARY - may change */
815 if (!is_integer(optarg))
816 usage2(_("Retries interval must be a positive integer"), optarg);
817 else
818 retries = atoi(optarg);
819 break; 688 break;
820 case 'o': /* object identifier */ 689 case 'o': /* object identifier */
821 if (strspn(optarg, "0123456789.,") != strlen(optarg)) { 690 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
@@ -824,306 +693,292 @@ int process_arguments(int argc, char **argv) {
824 * so we have a mib variable, rather than just an SNMP OID, 693 * so we have a mib variable, rather than just an SNMP OID,
825 * so we have to actually read the mib files 694 * so we have to actually read the mib files
826 */ 695 */
827 needmibs = true; 696 config.snmp_params.need_mibs = true;
828 }
829 for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) {
830 while (j >= oids_size) {
831 oids_size += OID_COUNT_STEP;
832 oids = realloc(oids, oids_size * sizeof(*oids));
833 }
834 oids[j] = strdup(ptr);
835 } 697 }
836 numoids = j; 698
837 if (option_char == 'E' || option_char == 'e') { 699 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
838 jj++; 700 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
839 while (j + 1 >= eval_size) { 701 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
840 eval_size += OID_COUNT_STEP;
841 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
842 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
843 }
844 if (option_char == 'E')
845 eval_method[j + 1] |= WARN_PRESENT;
846 else if (option_char == 'e')
847 eval_method[j + 1] |= CRIT_PRESENT;
848 } 702 }
849 break; 703 break;
850 case 'z': /* Null OID Return Check */ 704 case 'z': /* Null OID Return Check */
851 if (!is_integer(optarg)) 705 if (!is_integer(optarg)) {
852 usage2(_("Exit status must be a positive integer"), optarg); 706 usage2(_("Exit status must be a positive integer"), optarg);
853 else 707 } else {
854 nulloid = atoi(optarg); 708 config.evaluation_params.nulloid_result = atoi(optarg);
709 }
855 break; 710 break;
856 case 's': /* string or substring */ 711 case 's': /* string or substring */
857 strncpy(string_value, optarg, sizeof(string_value) - 1); 712 strncpy(config.evaluation_params.string_cmp_value, optarg,
858 string_value[sizeof(string_value) - 1] = 0; 713 sizeof(config.evaluation_params.string_cmp_value) - 1);
859 while (jj >= eval_size) { 714 config.evaluation_params
860 eval_size += OID_COUNT_STEP; 715 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
861 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 716 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
862 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
863 }
864 eval_method[jj++] = CRIT_STRING;
865 break; 717 break;
866 case 'R': /* regex */ 718 case 'R': /* regex */
867 cflags = REG_ICASE; 719 cflags = REG_ICASE;
868 // fall through 720 // fall through
869 case 'r': /* regex */ 721 case 'r': /* regex */
722 {
723 char regex_expect[MAX_INPUT_BUFFER] = "";
870 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 724 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
871 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); 725 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
872 regex_expect[sizeof(regex_expect) - 1] = 0; 726 regex_expect[sizeof(regex_expect) - 1] = 0;
873 errcode = regcomp(&preg, regex_expect, cflags); 727 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
874 if (errcode != 0) { 728 if (errcode != 0) {
875 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 729 char errbuf[MAX_INPUT_BUFFER] = "";
876 printf(_("Could Not Compile Regular Expression")); 730 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
877 return ERROR; 731 MAX_INPUT_BUFFER);
878 } 732 printf("Could Not Compile Regular Expression: %s", errbuf);
879 while (jj >= eval_size) { 733 process_arguments_wrapper result = {
880 eval_size += OID_COUNT_STEP; 734 .errorcode = ERROR,
881 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 735 };
882 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); 736 return result;
883 } 737 }
884 eval_method[jj++] = CRIT_REGEX; 738 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
885 break; 739 } break;
886
887 /* Format */
888 case 'd': /* delimiter */
889 delimiter = strscpy(delimiter, optarg);
890 break;
891 case 'D': /* output-delimiter */
892 output_delim = strscpy(output_delim, optarg);
893 break;
894 case 'l': /* label */ 740 case 'l': /* label */
895 nlabels++; 741 {
896 if (nlabels > labels_size) { 742 if (labels_counter >= config.snmp_params.num_of_test_units) {
897 labels_size += 8; 743 break;
898 labels = realloc(labels, labels_size * sizeof(*labels)); 744 }
899 if (labels == NULL) 745 char *ptr = trim_whitespaces_and_check_quoting(optarg);
900 die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); 746 if (ptr[0] == '\'') {
747 config.snmp_params.test_units[labels_counter].label = ptr + 1;
748 } else {
749 config.snmp_params.test_units[labels_counter].label = ptr;
901 } 750 }
902 labels[nlabels - 1] = optarg; 751
903 char *ptr = thisarg(optarg); 752 while (ptr && (ptr = get_next_argument(ptr))) {
904 labels[nlabels - 1] = ptr; 753 labels_counter++;
905 if (ptr[0] == '\'') 754 ptr = trim_whitespaces_and_check_quoting(ptr);
906 labels[nlabels - 1] = ptr + 1; 755 if (ptr[0] == '\'') {
907 while (ptr && (ptr = nextarg(ptr))) { 756 config.snmp_params.test_units[labels_counter].label = ptr + 1;
908 nlabels++; 757 } else {
909 if (nlabels > labels_size) { 758 config.snmp_params.test_units[labels_counter].label = ptr;
910 labels_size += 8;
911 labels = realloc(labels, labels_size * sizeof(*labels));
912 if (labels == NULL)
913 die(STATE_UNKNOWN, _("Could not reallocate labels\n"));
914 } 759 }
915 ptr = thisarg(ptr);
916 if (ptr[0] == '\'')
917 labels[nlabels - 1] = ptr + 1;
918 else
919 labels[nlabels - 1] = ptr;
920 } 760 }
921 break; 761 labels_counter++;
762 } break;
922 case 'u': /* units */ 763 case 'u': /* units */
923 units = optarg; 764 {
924 nunits++; 765 if (unitv_counter >= config.snmp_params.num_of_test_units) {
925 if (nunits > unitv_size) { 766 break;
926 unitv_size += 8;
927 unitv = realloc(unitv, unitv_size * sizeof(*unitv));
928 if (unitv == NULL)
929 die(STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
930 } 767 }
931 unitv[nunits - 1] = optarg; 768 char *ptr = trim_whitespaces_and_check_quoting(optarg);
932 ptr = thisarg(optarg); 769 if (ptr[0] == '\'') {
933 unitv[nunits - 1] = ptr; 770 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
934 if (ptr[0] == '\'') 771 } else {
935 unitv[nunits - 1] = ptr + 1; 772 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
936 while (ptr && (ptr = nextarg(ptr))) { 773 }
937 if (nunits > unitv_size) { 774 while (ptr && (ptr = get_next_argument(ptr))) {
938 unitv_size += 8; 775 unitv_counter++;
939 unitv = realloc(unitv, unitv_size * sizeof(*unitv)); 776 ptr = trim_whitespaces_and_check_quoting(ptr);
940 if (units == NULL) 777 if (ptr[0] == '\'') {
941 die(STATE_UNKNOWN, _("Could not realloc() units\n")); 778 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
779 } else {
780 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
942 } 781 }
943 nunits++;
944 ptr = thisarg(ptr);
945 if (ptr[0] == '\'')
946 unitv[nunits - 1] = ptr + 1;
947 else
948 unitv[nunits - 1] = ptr;
949 } 782 }
783 unitv_counter++;
784 } break;
785 case offset_index:
786 config.evaluation_params.offset = strtod(optarg, NULL);
787 config.evaluation_params.offset_set = true;
950 break; 788 break;
951 case L_CALCULATE_RATE: 789 case invert_search_index:
952 if (calculate_rate == 0) 790 config.evaluation_params.invert_search = false;
953 np_enable_state(NULL, 1);
954 calculate_rate = 1;
955 break;
956 case L_RATE_MULTIPLIER:
957 if (!is_integer(optarg) || ((rate_multiplier = atoi(optarg)) <= 0))
958 usage2(_("Rate multiplier must be a positive integer"), optarg);
959 break;
960 case L_OFFSET:
961 offset = strtod(optarg, NULL);
962 break;
963 case L_INVERT_SEARCH:
964 invert_search = 1;
965 break; 791 break;
966 case 'O': 792 case 'O':
967 perf_labels = 0; 793 config.evaluation_params.use_oid_as_perf_data_label = true;
968 break; 794 break;
969 case '4': 795 case '4':
796 // The default, do something here to be exclusive to -6 instead of doing nothing?
797 connection_prefix = "udp";
970 break; 798 break;
971 case '6': 799 case '6':
972 xasprintf(&ip_version, "udp6:"); 800 connection_prefix = "udp6";
973 if (verbose > 2) 801 break;
974 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); 802 case connection_prefix_index:
803 connection_prefix = optarg;
975 break; 804 break;
976 case 'M': 805 case 'M':
977 if (strspn(optarg, "0123456789.,") == strlen(optarg)) { 806 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
978 multiplier = strtod(optarg, NULL); 807 config.evaluation_params.multiplier = strtod(optarg, NULL);
808 config.evaluation_params.multiplier_set = true;
979 } 809 }
980 break; 810 break;
981 case 'f': 811 case ignore_mib_parsing_errors_index:
982 if (multiplier != 1.0) { 812 config.snmp_params.ignore_mib_parsing_errors = true;
983 fmtstr = optarg; 813 break;
984 fmtstr_set = true; 814 case 'f': // Deprecated format option for floating point values
815 break;
816 case output_format_index: {
817 parsed_output_format parser = mp_parse_output_format(optarg);
818 if (!parser.parsing_success) {
819 // TODO List all available formats here, maybe add anothoer usage function
820 printf("Invalid output format: %s\n", optarg);
821 exit(STATE_UNKNOWN);
822 }
823
824 config.output_format_is_set = true;
825 config.output_format = parser.output_format;
826 break;
827 }
828 case calculate_rate:
829 config.evaluation_params.calculate_rate = true;
830 break;
831 case rate_multiplier:
832 if (!is_integer(optarg) ||
833 ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) {
834 usage2(_("Rate multiplier must be a positive integer"), optarg);
985 } 835 }
986 break; 836 break;
987 case L_IGNORE_MIB_PARSING_ERRORS: 837 default:
988 ignore_mib_parsing_errors = true; 838 die(STATE_UNKNOWN, "Unknown option");
989 } 839 }
990 } 840 }
991 841
992 if (server_address == NULL) 842 if (config.snmp_params.snmp_session.peername == NULL) {
993 server_address = argv[optind]; 843 config.snmp_params.snmp_session.peername = argv[optind];
994 844 }
995 if (community == NULL)
996 community = strdup(DEFAULT_COMMUNITY);
997
998 return validate_arguments();
999}
1000
1001/******************************************************************************
1002
1003@@-
1004<sect3>
1005<title>validate_arguments</title>
1006
1007<para>&PROTO_validate_arguments;</para>
1008
1009<para>Checks to see if the default miblist needs to be loaded. Also verifies
1010the authentication and authorization combinations based on protocol version
1011selected.</para>
1012
1013<para></para>
1014
1015</sect3>
1016-@@
1017******************************************************************************/
1018 845
1019static int validate_arguments() { 846 // Build true peername here if necessary
1020 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 847 if (connection_prefix != NULL) {
1021 if (miblist == NULL) { 848 // We got something in the connection prefix
1022 if (needmibs) { 849 if (strcasecmp(connection_prefix, "udp") == 0) {
1023 miblist = strdup(DEFAULT_MIBLIST); 850 // The default, do nothing
851 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
852 // use tcp/ipv4
853 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
854 config.snmp_params.snmp_session.peername);
855 } else if (strcasecmp(connection_prefix, "tcp6") == 0 ||
856 strcasecmp(connection_prefix, "tcpv6") == 0 ||
857 strcasecmp(connection_prefix, "tcpipv6") == 0 ||
858 strcasecmp(connection_prefix, "udp6") == 0 ||
859 strcasecmp(connection_prefix, "udpipv6") == 0 ||
860 strcasecmp(connection_prefix, "udpv6") == 0) {
861 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it
862 // works anyway therefore do nothing here
863 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix,
864 config.snmp_params.snmp_session.peername);
865 } else if (strcmp(connection_prefix, "tls") == 0) {
866 // TODO: Anything else to do here?
867 xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s",
868 config.snmp_params.snmp_session.peername);
869 } else if (strcmp(connection_prefix, "dtls") == 0) {
870 // TODO: Anything else to do here?
871 xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s",
872 config.snmp_params.snmp_session.peername);
873 } else if (strcmp(connection_prefix, "unix") == 0) {
874 // TODO: Check whether this is a valid path?
875 xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s",
876 config.snmp_params.snmp_session.peername);
877 } else if (strcmp(connection_prefix, "ipx") == 0) {
878 xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s",
879 config.snmp_params.snmp_session.peername);
1024 } else { 880 } else {
1025 miblist = ""; /* don't read any mib files for numeric oids */ 881 // Don't know that prefix, die here
882 die(STATE_UNKNOWN, "Unknown connection prefix");
1026 } 883 }
1027 } 884 }
1028 885
1029 /* Check server_address is given */ 886 /* Check server_address is given */
1030 if (server_address == NULL) 887 if (config.snmp_params.snmp_session.peername == NULL) {
1031 die(STATE_UNKNOWN, _("No host specified\n")); 888 die(STATE_UNKNOWN, _("No host specified\n"));
889 }
1032 890
1033 /* Check oid is given */ 891 if (port != NULL) {
1034 if (numoids == 0) 892 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
1035 die(STATE_UNKNOWN, _("No OIDs specified\n")); 893 config.snmp_params.snmp_session.peername, port);
894 }
1036 895
1037 if (proto == NULL) 896 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1038 xasprintf(&proto, DEFAULT_PROTOCOL); 897 if (miblist == NULL) {
1039 898 if (config.snmp_params.need_mibs) {
1040 if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ 899 setenv("MIBLS", DEFAULT_MIBLIST, 1);
1041 numauthpriv = 2; 900 } else {
1042 authpriv = calloc(numauthpriv, sizeof(char *)); 901 setenv("MIBLS", "NONE", 1);
1043 authpriv[0] = strdup("-c"); 902 miblist = ""; /* don't read any mib files for numeric oids */
1044 authpriv[1] = strdup(community);
1045 } else if (strcmp(proto, "3") == 0) { /* snmpv3 args */
1046 if (!(context == NULL)) {
1047 numcontext = 2;
1048 contextargs = calloc(numcontext, sizeof(char *));
1049 contextargs[0] = strdup("-n");
1050 contextargs[1] = strdup(context);
1051 } 903 }
904 } else {
905 // Blatantly stolen from snmplib/snmp_parse_args
906 setenv("MIBS", miblist, 1);
907 }
1052 908
1053 if (seclevel == NULL) 909 // Historical default is SNMP v2c
1054 xasprintf(&seclevel, "noAuthNoPriv"); 910 if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) {
911 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
912 }
1055 913
1056 if (secname == NULL) 914 if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) ||
915 (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */
916 /*
917 config.numauthpriv = 2;
918 config.authpriv = calloc(config.numauthpriv, sizeof(char *));
919 config.authpriv[0] = strdup("-c");
920 config.authpriv[1] = strdup(community);
921 */
922 } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */
923 // generate keys for priv and auth here (if demanded)
924
925 if (config.snmp_params.snmp_session.securityName == NULL) {
1057 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 926 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
927 }
1058 928
1059 if (strcmp(seclevel, "noAuthNoPriv") == 0) { 929 switch (config.snmp_params.snmp_session.securityLevel) {
1060 numauthpriv = 4; 930 case SNMP_SEC_LEVEL_AUTHPRIV: {
1061 authpriv = calloc(numauthpriv, sizeof(char *)); 931 if (authpasswd == NULL) {
1062 authpriv[0] = strdup("-l"); 932 die(STATE_UNKNOWN,
1063 authpriv[1] = strdup("noAuthNoPriv"); 933 "No authentication passphrase was given, but authorization was requested");
1064 authpriv[2] = strdup("-u");
1065 authpriv[3] = strdup(secname);
1066 } else {
1067 if (!((strcmp(seclevel, "authNoPriv") == 0) || (strcmp(seclevel, "authPriv") == 0))) {
1068 usage2(_("Invalid seclevel"), seclevel);
1069 } 934 }
1070 935 // auth and priv
1071 if (authproto == NULL) 936 int priv_key_generated = generate_Ku(
1072 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); 937 config.snmp_params.snmp_session.securityPrivProto,
1073 938 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
1074 if (authpasswd == NULL) 939 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
1075 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); 940 &config.snmp_params.snmp_session.securityPrivKeyLen);
1076 941
1077 if (strcmp(seclevel, "authNoPriv") == 0) { 942 if (priv_key_generated != SNMPERR_SUCCESS) {
1078 numauthpriv = 8; 943 die(STATE_UNKNOWN, "Failed to generate privacy key");
1079 authpriv = calloc(numauthpriv, sizeof(char *));
1080 authpriv[0] = strdup("-l");
1081 authpriv[1] = strdup("authNoPriv");
1082 authpriv[2] = strdup("-a");
1083 authpriv[3] = strdup(authproto);
1084 authpriv[4] = strdup("-u");
1085 authpriv[5] = strdup(secname);
1086 authpriv[6] = strdup("-A");
1087 authpriv[7] = strdup(authpasswd);
1088 } else if (strcmp(seclevel, "authPriv") == 0) {
1089 if (privproto == NULL)
1090 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1091
1092 if (privpasswd == NULL)
1093 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1094
1095 numauthpriv = 12;
1096 authpriv = calloc(numauthpriv, sizeof(char *));
1097 authpriv[0] = strdup("-l");
1098 authpriv[1] = strdup("authPriv");
1099 authpriv[2] = strdup("-a");
1100 authpriv[3] = strdup(authproto);
1101 authpriv[4] = strdup("-u");
1102 authpriv[5] = strdup(secname);
1103 authpriv[6] = strdup("-A");
1104 authpriv[7] = strdup(authpasswd);
1105 authpriv[8] = strdup("-x");
1106 authpriv[9] = strdup(privproto);
1107 authpriv[10] = strdup("-X");
1108 authpriv[11] = strdup(privpasswd);
1109 } 944 }
1110 } 945 }
1111 946 // fall through
1112 } else { 947 case SNMP_SEC_LEVEL_AUTHNOPRIV: {
1113 usage2(_("Invalid SNMP version"), proto); 948 if (privpasswd == NULL) {
949 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested");
950 }
951 int auth_key_generated = generate_Ku(
952 config.snmp_params.snmp_session.securityAuthProto,
953 (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd,
954 strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey,
955 &config.snmp_params.snmp_session.securityAuthKeyLen);
956
957 if (auth_key_generated != SNMPERR_SUCCESS) {
958 die(STATE_UNKNOWN, "Failed to generate privacy key");
959 }
960 } break;
961 case SNMP_SEC_LEVEL_NOAUTH:
962 // No auth, no priv, not much todo
963 break;
964 }
1114 } 965 }
1115 966
1116 return OK; 967 process_arguments_wrapper result = {
968 .config = config,
969 .errorcode = OK,
970 };
971 return result;
1117} 972}
1118 973
1119/* trim leading whitespace 974/* trim leading whitespace
1120 if there is a leading quote, make sure it balances */ 975 if there is a leading quote, make sure it balances */
1121 976char *trim_whitespaces_and_check_quoting(char *str) {
1122static char *thisarg(char *str) {
1123 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ 977 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
1124 if (str[0] == '\'') { /* handle SIMPLE quoted strings */ 978 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1125 if (strlen(str) == 1 || !strstr(str + 1, "'")) 979 if (strlen(str) == 1 || !strstr(str + 1, "'")) {
1126 die(STATE_UNKNOWN, _("Unbalanced quotes\n")); 980 die(STATE_UNKNOWN, _("Unbalanced quotes\n"));
981 }
1127 } 982 }
1128 return str; 983 return str;
1129} 984}
@@ -1132,23 +987,21 @@ static char *thisarg(char *str) {
1132 set the trailing quote to '\x0' 987 set the trailing quote to '\x0'
1133 if the string continues, advance beyond the comma */ 988 if the string continues, advance beyond the comma */
1134 989
1135static char *nextarg(char *str) { 990char *get_next_argument(char *str) {
1136 if (str[0] == '\'') { 991 if (str[0] == '\'') {
1137 str[0] = 0; 992 str[0] = 0;
1138 if (strlen(str) > 1) { 993 if (strlen(str) > 1) {
1139 str = strstr(str + 1, "'"); 994 str = strstr(str + 1, "'");
1140 return (++str); 995 return (++str);
1141 } else {
1142 return NULL;
1143 } 996 }
997 return NULL;
1144 } 998 }
1145 if (str[0] == ',') { 999 if (str[0] == ',') {
1146 str[0] = 0; 1000 str[0] = 0;
1147 if (strlen(str) > 1) { 1001 if (strlen(str) > 1) {
1148 return (++str); 1002 return (++str);
1149 } else {
1150 return NULL;
1151 } 1003 }
1004 return NULL;
1152 } 1005 }
1153 if ((str = strstr(str, ",")) && strlen(str) > 1) { 1006 if ((str = strstr(str, ",")) && strlen(str) > 1) {
1154 str[0] = 0; 1007 str[0] = 0;
@@ -1157,41 +1010,7 @@ static char *nextarg(char *str) {
1157 return NULL; 1010 return NULL;
1158} 1011}
1159 1012
1160/* multiply result (values 0 < n < 1 work as divider) */ 1013void print_help(void) {
1161static char *multiply(char *str) {
1162 if (multiplier == 1)
1163 return (str);
1164
1165 if (verbose > 2)
1166 printf(" multiply input: %s\n", str);
1167
1168 char *endptr;
1169 double val = strtod(str, &endptr);
1170 if ((val == 0.0) && (endptr == str)) {
1171 die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str);
1172 }
1173
1174 if (verbose > 2)
1175 printf(" multiply extracted double: %f\n", val);
1176
1177 val *= multiplier;
1178 char *conv = "%f";
1179 if (fmtstr_set) {
1180 conv = fmtstr;
1181 }
1182 if (val == (int)val) {
1183 snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val);
1184 } else {
1185 if (verbose > 2)
1186 printf(" multiply using format: %s\n", conv);
1187 snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val);
1188 }
1189 if (verbose > 2)
1190 printf(" multiply result: %s\n", buffer);
1191 return buffer;
1192}
1193
1194static void print_help(void) {
1195 print_revision(progname, NP_VERSION); 1014 print_revision(progname, NP_VERSION);
1196 1015
1197 printf(COPYRIGHT, copyright, email); 1016 printf(COPYRIGHT, copyright, email);
@@ -1204,8 +1023,6 @@ static void print_help(void) {
1204 1023
1205 printf(UT_HELP_VRSN); 1024 printf(UT_HELP_VRSN);
1206 printf(UT_EXTRA_OPTS); 1025 printf(UT_EXTRA_OPTS);
1207 printf(UT_IPv46);
1208
1209 printf(UT_HOST_PORT, 'p', DEFAULT_PORT); 1026 printf(UT_HOST_PORT, 'p', DEFAULT_PORT);
1210 1027
1211 /* SNMP and Authentication Protocol */ 1028 /* SNMP and Authentication Protocol */
@@ -1217,13 +1034,15 @@ static void print_help(void) {
1217 printf(" %s\n", _("SNMPv3 context")); 1034 printf(" %s\n", _("SNMPv3 context"));
1218 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); 1035 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1219 printf(" %s\n", _("SNMPv3 securityLevel")); 1036 printf(" %s\n", _("SNMPv3 securityLevel"));
1220 printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); 1037 printf(" %s\n", "-a, --authproto=[MD5|SHA]");
1221 printf(" %s\n", 1038 printf(" %s\n", _("SNMPv3 auth proto"));
1222 _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); 1039#ifdef HAVE_USM_DES_PRIV_PROTOCOL
1223 printf(" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); 1040 printf(" %s\n", "-x, --privproto=[DES|AES]");
1224 printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); 1041 printf(" %s\n", _("SNMPv3 priv proto (default DES)"));
1225 printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); 1042#else
1226 printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); 1043 printf(" %s\n", "-x, --privproto=[AES]");
1044 printf(" %s\n", _("SNMPv3 priv proto (default AES)"));
1045#endif
1227 1046
1228 /* Authentication Tokens*/ 1047 /* Authentication Tokens*/
1229 printf(" %s\n", "-C, --community=STRING"); 1048 printf(" %s\n", "-C, --community=STRING");
@@ -1235,15 +1054,18 @@ static void print_help(void) {
1235 printf(" %s\n", _("SNMPv3 authentication password")); 1054 printf(" %s\n", _("SNMPv3 authentication password"));
1236 printf(" %s\n", "-X, --privpasswd=PASSWORD"); 1055 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1237 printf(" %s\n", _("SNMPv3 privacy password")); 1056 printf(" %s\n", _("SNMPv3 privacy password"));
1057 printf(" %s\n", "--connection-prefix");
1058 printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, "
1059 "tcp6, tcpv6, tcpipv6, tls, dtls - "
1060 "default is \"udp\"\n");
1238 1061
1239 /* OID Stuff */ 1062 /* OID Stuff */
1240 printf(" %s\n", "-o, --oid=OID(s)"); 1063 printf(" %s\n", "-o, --oid=OID(s)");
1241 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); 1064 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1242 printf(" %s\n", "-m, --miblist=STRING"); 1065 printf(" %s\n", "-m, --miblist=STRING");
1243 printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); 1066 printf(" %s\n",
1067 _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1244 printf(" %s\n", _("for symbolic OIDs.)")); 1068 printf(" %s\n", _("for symbolic OIDs.)"));
1245 printf(" %s\n", "-d, --delimiter=STRING");
1246 printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1247 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); 1069 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1248 printf(" %s\n", _("to be the data that should be used in the evaluation.")); 1070 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1249 printf(" %s\n", "-z, --nulloid=#"); 1071 printf(" %s\n", "-z, --nulloid=#");
@@ -1260,10 +1082,6 @@ static void print_help(void) {
1260 printf(" %s\n", _("Warning threshold range(s)")); 1082 printf(" %s\n", _("Warning threshold range(s)"));
1261 printf(" %s\n", "-c, --critical=THRESHOLD(s)"); 1083 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1262 printf(" %s\n", _("Critical threshold range(s)")); 1084 printf(" %s\n", _("Critical threshold range(s)"));
1263 printf(" %s\n", "--rate");
1264 printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1265 printf(" %s\n", "--rate-multiplier");
1266 printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1267 printf(" %s\n", "--offset=OFFSET"); 1085 printf(" %s\n", "--offset=OFFSET");
1268 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); 1086 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1269 1087
@@ -1271,9 +1089,11 @@ static void print_help(void) {
1271 printf(" %s\n", "-s, --string=STRING"); 1089 printf(" %s\n", "-s, --string=STRING");
1272 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); 1090 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1273 printf(" %s\n", "-r, --ereg=REGEX"); 1091 printf(" %s\n", "-r, --ereg=REGEX");
1274 printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); 1092 printf(" %s\n",
1093 _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1275 printf(" %s\n", "-R, --eregi=REGEX"); 1094 printf(" %s\n", "-R, --eregi=REGEX");
1276 printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); 1095 printf(" %s\n",
1096 _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1277 printf(" %s\n", "--invert-search"); 1097 printf(" %s\n", "--invert-search");
1278 printf(" %s\n", _("Invert search result (CRITICAL if found)")); 1098 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1279 1099
@@ -1282,53 +1102,46 @@ static void print_help(void) {
1282 printf(" %s\n", _("Prefix label for output from plugin")); 1102 printf(" %s\n", _("Prefix label for output from plugin"));
1283 printf(" %s\n", "-u, --units=STRING"); 1103 printf(" %s\n", "-u, --units=STRING");
1284 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); 1104 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1285 printf(" %s\n", "-D, --output-delimiter=STRING");
1286 printf(" %s\n", _("Separates output on multiple OID requests"));
1287 printf(" %s\n", "-M, --multiplier=FLOAT"); 1105 printf(" %s\n", "-M, --multiplier=FLOAT");
1288 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); 1106 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1289 printf(" %s\n", "-f, --fmtstr=STRING"); 1107 printf(UT_OUTPUT_FORMAT);
1290 printf(" %s\n", _("C-style format string for float values (see option -M)"));
1291 1108
1292 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1109 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1293 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5")); 1110 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: "
1111 "timeout_interval * retries + 5"));
1294 printf(" %s\n", "-e, --retries=INTEGER"); 1112 printf(" %s\n", "-e, --retries=INTEGER");
1295 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); 1113 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "),
1114 DEFAULT_RETRIES);
1296 1115
1297 printf(" %s\n", "-O, --perf-oids"); 1116 printf(" %s\n", "-O, --perf-oids");
1298 printf(" %s\n", _("Label performance data with OIDs instead of --label's")); 1117 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1299 1118
1300 printf(" %s\n", "--ignore-mib-parsing-errors"); 1119 printf(" %s\n", "--ignore-mib-parsing-errors");
1301 printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); 1120 printf(" %s\n", _("Do to not print errors encountered when parsing MIB files"));
1302 1121
1303 printf(UT_VERBOSE); 1122 printf(UT_VERBOSE);
1304 1123
1305 printf("\n"); 1124 printf("\n");
1306 printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); 1125 printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries."));
1307 printf("%s\n", _("if you don't have the package installed, you will need to download it from")); 1126 printf("%s\n",
1127 _("if you don't have the libraries installed, you will need to download them from"));
1308 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); 1128 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1309 1129
1310 printf("\n"); 1130 printf("\n");
1311 printf("%s\n", _("Notes:")); 1131 printf("%s\n", _("Notes:"));
1312 printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); 1132 printf(" %s\n",
1133 _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1313 printf(" %s\n", _("list (lists with internal spaces must be quoted).")); 1134 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1314 1135
1315 printf(" -%s", UT_THRESHOLDS_NOTES); 1136 printf(" -%s", UT_THRESHOLDS_NOTES);
1316 1137
1317 printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); 1138 printf(" %s\n",
1139 _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1318 printf(" %s\n", _("- Note that only one string and one regex may be checked at present")); 1140 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1319 printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); 1141 printf(" %s\n",
1142 _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1320 printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); 1143 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1321 1144
1322 printf("\n");
1323 printf("%s\n", _("Rate Calculation:"));
1324 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1325 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1326 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1327 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1328 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1329 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1330 printf(" %s\n", _("changing the arguments will create a new state file."));
1331
1332 printf(UT_SUPPORT); 1145 printf(UT_SUPPORT);
1333} 1146}
1334 1147
@@ -1339,5 +1152,5 @@ void print_usage(void) {
1339 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); 1152 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1340 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); 1153 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1341 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); 1154 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1342 printf("[-M multiplier [-f format]]\n"); 1155 printf("[-M multiplier]\n");
1343} 1156}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c
new file mode 100644
index 00000000..ecbfc5dd
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.c
@@ -0,0 +1,934 @@
1#include "./check_snmp_helpers.h"
2#include <string.h>
3#include "../../lib/utils_base.h"
4#include "config.h"
5#include <assert.h>
6#include "../utils.h"
7#include "output.h"
8#include "states.h"
9#include <sys/stat.h>
10#include <ctype.h>
11
12extern int verbose;
13
14check_snmp_test_unit check_snmp_test_unit_init() {
15 check_snmp_test_unit tmp = {
16 .threshold = mp_thresholds_init(),
17 };
18 return tmp;
19}
20
21int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit test_units[],
22 size_t max_test_units, bool is_critical) {
23
24 if (threshold_string == NULL || strlen(threshold_string) == 0) {
25 // No input, do nothing
26 return 0;
27 }
28
29 if (strchr(threshold_string, ',') != NULL) {
30 // Got a comma in the string, should be multiple values
31 size_t tu_index = 0;
32
33 while (threshold_string[0] == ',') {
34 // got commas at the beginning, so skip some values
35 tu_index++;
36 threshold_string++;
37 }
38
39 for (char *ptr = strtok(threshold_string, ", "); ptr != NULL;
40 ptr = strtok(NULL, ", "), tu_index++) {
41
42 if (tu_index > max_test_units) {
43 // More thresholds then values, just ignore them
44 return 0;
45 }
46
47 // edge case: maybe we got `,,` to skip a value
48 if (strlen(ptr) == 0) {
49 // no threshold given, do not set it then
50 continue;
51 }
52
53 mp_range_parsed tmp = mp_parse_range_string(ptr);
54 if (tmp.error != MP_PARSING_SUCCES) {
55 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr);
56 }
57
58 if (is_critical) {
59 test_units[tu_index].threshold.critical = tmp.range;
60 test_units[tu_index].threshold.critical_is_set = true;
61 } else {
62 test_units[tu_index].threshold.warning = tmp.range;
63 test_units[tu_index].threshold.warning_is_set = true;
64 }
65 }
66
67 } else {
68 // Single value
69 // only valid for the first test unit
70 mp_range_parsed tmp = mp_parse_range_string(threshold_string);
71 if (tmp.error != MP_PARSING_SUCCES) {
72 die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string);
73 }
74
75 if (is_critical) {
76 test_units[0].threshold.critical = tmp.range;
77 test_units[0].threshold.critical_is_set = true;
78 } else {
79 test_units[0].threshold.warning = tmp.range;
80 test_units[0].threshold.warning_is_set = true;
81 }
82 }
83
84 return 0;
85}
86
87const int DEFAULT_PROTOCOL = SNMP_VERSION_1;
88const char DEFAULT_OUTPUT_DELIMITER[] = " ";
89
90const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192;
91
92check_snmp_config check_snmp_config_init() {
93 check_snmp_config tmp = {
94 .snmp_params =
95 {
96 .use_getnext = false,
97
98 .ignore_mib_parsing_errors = false,
99 .need_mibs = false,
100
101 .test_units = NULL,
102 .num_of_test_units = 0,
103 },
104
105 .evaluation_params =
106 {
107 .nulloid_result = STATE_UNKNOWN, // state to return if no result for query
108
109 .invert_search = true,
110 .regex_cmp_value = {},
111 .string_cmp_value = "",
112
113 .multiplier = 1.0,
114 .multiplier_set = false,
115 .offset = 0,
116 .offset_set = false,
117
118 .use_oid_as_perf_data_label = false,
119
120 .calculate_rate = false,
121 .rate_multiplier = 1,
122 },
123 };
124
125 snmp_sess_init(&tmp.snmp_params.snmp_session);
126
127 tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES;
128 tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION;
129 tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
130 tmp.snmp_params.snmp_session.community = (unsigned char *)"public";
131 tmp.snmp_params.snmp_session.community_len = strlen("public");
132 return tmp;
133}
134
135snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) {
136 if (parameters.ignore_mib_parsing_errors) {
137 char *opt_toggle_res = snmp_mib_toggle_options("e");
138 if (opt_toggle_res != NULL) {
139 die(STATE_UNKNOWN, "Unable to disable MIB parsing errors");
140 }
141 }
142
143 struct snmp_pdu *pdu = NULL;
144 if (parameters.use_getnext) {
145 pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
146 } else {
147 pdu = snmp_pdu_create(SNMP_MSG_GET);
148 }
149
150 for (size_t i = 0; i < parameters.num_of_test_units; i++) {
151 assert(parameters.test_units[i].oid != NULL);
152 if (verbose > 0) {
153 printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid);
154 }
155
156 oid tmp_OID[MAX_OID_LEN];
157 size_t tmp_OID_len = MAX_OID_LEN;
158 if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) {
159 // success
160 snmp_add_null_var(pdu, tmp_OID, tmp_OID_len);
161 } else {
162 // failed
163 snmp_perror("Parsing failure");
164 die(STATE_UNKNOWN, "Failed to parse OID\n");
165 }
166 }
167
168 const int timeout_safety_tolerance = 5;
169 alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) +
170 timeout_safety_tolerance);
171
172 struct snmp_session *active_session = snmp_open(&parameters.snmp_session);
173 if (active_session == NULL) {
174 int pcliberr = 0;
175 int psnmperr = 0;
176 char *pperrstring = NULL;
177 snmp_error(&parameters.snmp_session, &pcliberr, &psnmperr, &pperrstring);
178 die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring);
179 }
180
181 struct snmp_pdu *response = NULL;
182 int snmp_query_status = snmp_synch_response(active_session, pdu, &response);
183
184 if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) {
185 int pcliberr = 0;
186 int psnmperr = 0;
187 char *pperrstring = NULL;
188 snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring);
189
190 if (psnmperr == SNMPERR_TIMEOUT) {
191 // We exit with critical here for some historical reason
192 die(STATE_CRITICAL, "SNMP query ran into a timeout\n");
193 }
194 die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring);
195 }
196
197 snmp_close(active_session);
198
199 /* disable alarm again */
200 alarm(0);
201
202 snmp_responces result = {
203 .errorcode = OK,
204 .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)),
205 };
206
207 if (result.response_values == NULL) {
208 result.errorcode = ERROR;
209 return result;
210 }
211
212 // We got the the query results, now process them
213 size_t loop_index = 0;
214 for (netsnmp_variable_list *vars = response->variables; vars;
215 vars = vars->next_variable, loop_index++) {
216
217 for (size_t jdx = 0; jdx < vars->name_length; jdx++) {
218 result.response_values[loop_index].oid[jdx] = vars->name[jdx];
219 }
220 result.response_values[loop_index].oid_length = vars->name_length;
221
222 switch (vars->type) {
223 case ASN_OCTET_STR: {
224 result.response_values[loop_index].string_response = strdup((char *)vars->val.string);
225 result.response_values[loop_index].type = vars->type;
226 if (verbose) {
227 printf("Debug: Got a string as response: %s\n", vars->val.string);
228 }
229 }
230 continue;
231 case ASN_OPAQUE:
232 if (verbose) {
233 printf("Debug: Got OPAQUE\n");
234 }
235 break;
236 /* Numerical values */
237 case ASN_COUNTER64: {
238 if (verbose) {
239 printf("Debug: Got counter64\n");
240 }
241 struct counter64 tmp = *(vars->val.counter64);
242 uint64_t counter = (tmp.high << 32) + tmp.low;
243 result.response_values[loop_index].value.uIntVal = counter;
244 result.response_values[loop_index].type = vars->type;
245 } break;
246 case ASN_GAUGE: // same as ASN_UNSIGNED
247 case ASN_TIMETICKS:
248 case ASN_COUNTER:
249 case ASN_UINTEGER: {
250 if (verbose) {
251 printf("Debug: Got a Integer like\n");
252 }
253 result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer);
254 result.response_values[loop_index].type = vars->type;
255 } break;
256 case ASN_INTEGER: {
257 if (verbose) {
258 printf("Debug: Got a Integer\n");
259 }
260 result.response_values[loop_index].value.intVal = *(vars->val.integer);
261 result.response_values[loop_index].type = vars->type;
262 } break;
263 case ASN_FLOAT: {
264 if (verbose) {
265 printf("Debug: Got a float\n");
266 }
267 result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal);
268 result.response_values[loop_index].type = vars->type;
269 } break;
270 case ASN_DOUBLE: {
271 if (verbose) {
272 printf("Debug: Got a double\n");
273 }
274 result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal);
275 result.response_values[loop_index].type = vars->type;
276 } break;
277 case ASN_IPADDRESS:
278 if (verbose) {
279 printf("Debug: Got an IP address\n");
280 }
281 result.response_values[loop_index].type = vars->type;
282
283 // TODO: print address here, state always ok? or regex match?
284 break;
285 default:
286 if (verbose) {
287 printf("Debug: Got a unmatched result type: %hhu\n", vars->type);
288 }
289 // TODO: Error here?
290 break;
291 }
292 }
293
294 return result;
295}
296
297check_snmp_evaluation evaluate_single_unit(response_value response,
298 check_snmp_evaluation_parameters eval_params,
299 check_snmp_test_unit test_unit, time_t query_timestamp,
300 check_snmp_state_entry prev_state,
301 bool have_previous_state) {
302 mp_subcheck sc_oid_test = mp_subcheck_init();
303
304 if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) {
305 xasprintf(&sc_oid_test.output, "%s - ", test_unit.label);
306 } else {
307 sc_oid_test.output = strdup("");
308 }
309
310 char oid_string[(MAX_OID_LEN * 2) + 1] = {};
311
312 int oid_string_result =
313 snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length);
314 if (oid_string_result <= 0) {
315 // TODO error here
316 die(STATE_UNKNOWN, "snprint_objid failed\n");
317 }
318
319 xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string);
320 sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK);
321
322 if (verbose > 2) {
323 printf("Processing oid %s\n", oid_string);
324 }
325
326 bool got_a_numerical_value = false;
327 mp_perfdata_value pd_result_val = {0};
328
329 check_snmp_state_entry result_state = {
330 .timestamp = query_timestamp,
331 .oid_length = response.oid_length,
332 .type = response.type,
333 };
334
335 for (size_t i = 0; i < response.oid_length; i++) {
336 result_state.oid[i] = response.oid[i];
337 }
338
339 if (have_previous_state) {
340 if (query_timestamp == prev_state.timestamp) {
341 // somehow we have the same timestamp again, that can't be good
342 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN);
343 xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid");
344
345 check_snmp_evaluation result = {
346 .sc = sc_oid_test,
347 .state = result_state,
348 };
349
350 return result;
351 }
352 }
353 // compute rate time difference
354 double timeDiff = 0;
355 if (have_previous_state) {
356 if (verbose) {
357 printf("Previous timestamp: %s", ctime(&prev_state.timestamp));
358 printf("Current timestamp: %s", ctime(&query_timestamp));
359 }
360 timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier;
361 }
362
363 mp_perfdata pd_num_val = {};
364
365 switch (response.type) {
366 case ASN_OCTET_STR: {
367 char *tmp = response.string_response;
368 if (strchr(tmp, '"') != NULL) {
369 // got double quote in the string
370 if (strchr(tmp, '\'') != NULL) {
371 // got single quote in the string too
372 // dont quote that at all to avoid even more confusion
373 xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp);
374 } else {
375 // quote with single quotes
376 xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp);
377 }
378 } else {
379 // quote with double quotes
380 xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp);
381 }
382
383 if (strlen(tmp) == 0) {
384 sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result);
385 }
386
387 // String matching test
388 if ((test_unit.eval_mthd.crit_string)) {
389 if (strcmp(tmp, eval_params.string_cmp_value)) {
390 sc_oid_test = mp_set_subcheck_state(
391 sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK);
392 } else {
393 sc_oid_test = mp_set_subcheck_state(
394 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
395 }
396 } else if (test_unit.eval_mthd.crit_regex) {
397 const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1;
398 regmatch_t pmatch[nmatch];
399 memset(pmatch, '\0', sizeof(regmatch_t) * nmatch);
400
401 int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0);
402 if (excode == 0) {
403 sc_oid_test = mp_set_subcheck_state(
404 sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL);
405 } else if (excode != REG_NOMATCH) {
406 char errbuf[MAX_INPUT_BUFFER] = "";
407 regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER);
408 printf(_("Execute Error: %s\n"), errbuf);
409 exit(STATE_CRITICAL);
410 } else { // REG_NOMATCH
411 sc_oid_test = mp_set_subcheck_state(
412 sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK);
413 }
414 }
415 } break;
416 case ASN_COUNTER64:
417 got_a_numerical_value = true;
418
419 result_state.value.uIntVal = response.value.uIntVal;
420 result_state.type = response.type;
421
422 // TODO: perfdata unit counter
423 if (eval_params.calculate_rate && have_previous_state) {
424 if (prev_state.value.uIntVal > response.value.uIntVal) {
425 // overflow
426 unsigned long long tmp =
427 (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal;
428
429 tmp /= timeDiff;
430 pd_result_val = mp_create_pd_value(tmp);
431 } else {
432 pd_result_val = mp_create_pd_value(
433 (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff);
434 }
435 } else {
436 // It's only a counter if we cont compute rate
437 pd_num_val.uom = "c";
438 pd_result_val = mp_create_pd_value(response.value.uIntVal);
439 }
440 break;
441 case ASN_GAUGE: // same as ASN_UNSIGNED
442 case ASN_TIMETICKS:
443 case ASN_COUNTER:
444 case ASN_UINTEGER: {
445 got_a_numerical_value = true;
446 long long treated_value = (long long)response.value.uIntVal;
447
448 if (eval_params.multiplier_set || eval_params.offset_set) {
449 double processed = 0;
450 if (eval_params.offset_set) {
451 processed += eval_params.offset;
452 }
453
454 if (eval_params.multiplier_set) {
455 processed = processed * eval_params.multiplier;
456 }
457
458 treated_value = lround(processed);
459 }
460
461 result_state.value.intVal = treated_value;
462
463 if (eval_params.calculate_rate && have_previous_state) {
464 if (verbose > 2) {
465 printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__,
466 prev_state.value.intVal);
467 printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__,
468 treated_value);
469 }
470 double rate = (treated_value - prev_state.value.intVal) / timeDiff;
471 pd_result_val = mp_create_pd_value(rate);
472 } else {
473 pd_result_val = mp_create_pd_value(treated_value);
474
475 if (response.type == ASN_COUNTER) {
476 pd_num_val.uom = "c";
477 }
478 }
479
480 } break;
481 case ASN_INTEGER: {
482 if (eval_params.multiplier_set || eval_params.offset_set) {
483 double processed = 0;
484 if (eval_params.multiplier_set) {
485 processed = (double)response.value.intVal * eval_params.multiplier;
486 }
487
488 if (eval_params.offset_set) {
489 processed += eval_params.offset;
490 }
491
492 result_state.value.doubleVal = processed;
493
494 if (eval_params.calculate_rate && have_previous_state) {
495 pd_result_val =
496 mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff);
497 } else {
498 pd_result_val = mp_create_pd_value(processed);
499 }
500 } else {
501 result_state.value.intVal = response.value.intVal;
502
503 if (eval_params.calculate_rate && have_previous_state) {
504 pd_result_val = mp_create_pd_value(
505 (response.value.intVal - prev_state.value.intVal) / timeDiff);
506 } else {
507 pd_result_val = mp_create_pd_value(response.value.intVal);
508 }
509 }
510
511 got_a_numerical_value = true;
512 } break;
513 case ASN_FLOAT: // fallthrough
514 case ASN_DOUBLE: {
515 got_a_numerical_value = true;
516 double tmp = response.value.doubleVal;
517 if (eval_params.offset_set) {
518 tmp += eval_params.offset;
519 }
520
521 if (eval_params.multiplier_set) {
522 tmp *= eval_params.multiplier;
523 }
524
525 if (eval_params.calculate_rate && have_previous_state) {
526 pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff);
527 } else {
528 pd_result_val = mp_create_pd_value(tmp);
529 }
530 got_a_numerical_value = true;
531
532 result_state.value.doubleVal = tmp;
533 } break;
534 case ASN_IPADDRESS:
535 // TODO
536 break;
537 }
538
539 if (got_a_numerical_value) {
540 if (eval_params.use_oid_as_perf_data_label) {
541 // Use oid for perdata label
542 pd_num_val.label = strdup(oid_string);
543 // TODO strdup error checking
544 } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) {
545 pd_num_val.label = strdup(test_unit.label);
546 } else {
547 pd_num_val.label = strdup(test_unit.oid);
548 }
549
550 if (!(eval_params.calculate_rate && !have_previous_state)) {
551 // some kind of numerical value
552 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
553 pd_num_val.uom = test_unit.unit_value;
554 }
555
556 pd_num_val.value = pd_result_val;
557
558 xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output,
559 pd_value_to_string(pd_result_val));
560
561 if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) {
562 xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value);
563 }
564
565 if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) {
566 pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold);
567 mp_state_enum tmp_state = mp_get_pd_status(pd_num_val);
568
569 if (tmp_state == STATE_WARNING) {
570 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING);
571 xasprintf(&sc_oid_test.output, "%s - number violates warning threshold",
572 sc_oid_test.output);
573 } else if (tmp_state == STATE_CRITICAL) {
574 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL);
575 xasprintf(&sc_oid_test.output, "%s - number violates critical threshold",
576 sc_oid_test.output);
577 }
578 }
579
580 mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val);
581 } else {
582 // should calculate rate, but there is no previous state, so first run
583 // exit with ok now
584 sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK);
585 xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay",
586 sc_oid_test.output);
587 }
588 }
589
590 check_snmp_evaluation result = {
591 .sc = sc_oid_test,
592 .state = result_state,
593 };
594
595 return result;
596}
597
598char *_np_state_generate_key(int argc, char **argv);
599
600/*
601 * If time=NULL, use current time. Create state file, with state format
602 * version, default text. Writes version, time, and data. Avoid locking
603 * problems - use mv to write and then swap. Possible loss of state data if
604 * two things writing to same key at same time.
605 * Will die with UNKNOWN if errors
606 */
607void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) {
608 time_t current_time;
609 if (timestamp == 0) {
610 time(&current_time);
611 } else {
612 current_time = timestamp;
613 }
614
615 int result = 0;
616
617 /* If file doesn't currently exist, create directories */
618 if (access(stateKey._filename, F_OK) != 0) {
619 char *directories = NULL;
620 result = asprintf(&directories, "%s", stateKey._filename);
621 if (result < 0) {
622 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
623 }
624
625 for (char *p = directories + 1; *p; p++) {
626 if (*p == '/') {
627 *p = '\0';
628 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
629 /* Can't free this! Otherwise error message is wrong! */
630 /* np_free(directories); */
631 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
632 }
633 *p = '/';
634 }
635 }
636
637 if (directories) {
638 free(directories);
639 }
640 }
641
642 char *temp_file = NULL;
643 result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename);
644 if (result < 0) {
645 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
646 }
647
648 int temp_file_desc = 0;
649 if ((temp_file_desc = mkstemp(temp_file)) == -1) {
650 if (temp_file) {
651 free(temp_file);
652 }
653 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
654 }
655
656 FILE *temp_file_pointer = fdopen(temp_file_desc, "w");
657 if (temp_file_pointer == NULL) {
658 close(temp_file_desc);
659 unlink(temp_file);
660 if (temp_file) {
661 free(temp_file);
662 }
663 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
664 }
665
666 fprintf(temp_file_pointer, "# NP State file\n");
667 fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION);
668 fprintf(temp_file_pointer, "%d\n", stateKey.data_version);
669 fprintf(temp_file_pointer, "%lu\n", current_time);
670 fprintf(temp_file_pointer, "%s\n", stringToStore);
671
672 fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP);
673
674 fflush(temp_file_pointer);
675
676 result = fclose(temp_file_pointer);
677
678 fsync(temp_file_desc);
679
680 if (result != 0) {
681 unlink(temp_file);
682 if (temp_file) {
683 free(temp_file);
684 }
685 die(STATE_UNKNOWN, _("Error writing temp file"));
686 }
687
688 if (rename(temp_file, stateKey._filename) != 0) {
689 unlink(temp_file);
690 if (temp_file) {
691 free(temp_file);
692 }
693 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
694 }
695
696 if (temp_file) {
697 free(temp_file);
698 }
699}
700
701/*
702 * Read the state file
703 */
704bool _np_state_read_file(FILE *state_file, state_key stateKey) {
705 time_t current_time;
706 time(&current_time);
707
708 /* Note: This introduces a limit of 8192 bytes in the string data */
709 char *line = (char *)calloc(1, 8192);
710 if (line == NULL) {
711 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
712 }
713
714 bool status = false;
715 enum {
716 STATE_FILE_VERSION,
717 STATE_DATA_VERSION,
718 STATE_DATA_TIME,
719 STATE_DATA_TEXT,
720 STATE_DATA_END
721 } expected = STATE_FILE_VERSION;
722
723 int failure = 0;
724 while (!failure && (fgets(line, 8192, state_file)) != NULL) {
725 size_t pos = strlen(line);
726 if (line[pos - 1] == '\n') {
727 line[pos - 1] = '\0';
728 }
729
730 if (line[0] == '#') {
731 continue;
732 }
733
734 switch (expected) {
735 case STATE_FILE_VERSION: {
736 int i = atoi(line);
737 if (i != NP_STATE_FORMAT_VERSION) {
738 failure++;
739 } else {
740 expected = STATE_DATA_VERSION;
741 }
742 } break;
743 case STATE_DATA_VERSION: {
744 int i = atoi(line);
745 if (i != stateKey.data_version) {
746 failure++;
747 } else {
748 expected = STATE_DATA_TIME;
749 }
750 } break;
751 case STATE_DATA_TIME: {
752 /* If time > now, error */
753 time_t data_time = strtoul(line, NULL, 10);
754 if (data_time > current_time) {
755 failure++;
756 } else {
757 stateKey.state_data->time = data_time;
758 expected = STATE_DATA_TEXT;
759 }
760 } break;
761 case STATE_DATA_TEXT:
762 stateKey.state_data->data = strdup(line);
763 if (stateKey.state_data->data == NULL) {
764 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
765 }
766 stateKey.state_data->length = strlen(line);
767 expected = STATE_DATA_END;
768 status = true;
769 break;
770 case STATE_DATA_END:;
771 }
772 }
773
774 if (line) {
775 free(line);
776 }
777 return status;
778}
779/*
780 * Will return NULL if no data is available (first run). If key currently
781 * exists, read data. If state file format version is not expected, return
782 * as if no data. Get state data version number and compares to expected.
783 * If numerically lower, then return as no previous state. die with UNKNOWN
784 * if exceptional error.
785 */
786state_data *np_state_read(state_key stateKey) {
787 /* Open file. If this fails, no previous state found */
788 FILE *statefile = fopen(stateKey._filename, "r");
789 state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data));
790 if (statefile != NULL) {
791
792 if (this_state_data == NULL) {
793 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
794 }
795
796 this_state_data->data = NULL;
797 stateKey.state_data = this_state_data;
798
799 if (_np_state_read_file(statefile, stateKey)) {
800 this_state_data->errorcode = OK;
801 } else {
802 this_state_data->errorcode = ERROR;
803 }
804
805 fclose(statefile);
806 } else {
807 // Failed to open state file
808 this_state_data->errorcode = ERROR;
809 }
810
811 return stateKey.state_data;
812}
813
814/*
815 * Internal function. Returns either:
816 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
817 * statically compiled shared state directory
818 */
819char *_np_state_calculate_location_prefix(void) {
820 char *env_dir;
821
822 /* Do not allow passing MP_STATE_PATH in setuid plugins
823 * for security reasons */
824 if (!mp_suid()) {
825 env_dir = getenv("MP_STATE_PATH");
826 if (env_dir && env_dir[0] != '\0') {
827 return env_dir;
828 }
829 /* This is the former ENV, for backward-compatibility */
830 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
831 if (env_dir && env_dir[0] != '\0') {
832 return env_dir;
833 }
834 }
835
836 return NP_STATE_DIR_PREFIX;
837}
838
839/*
840 * Initiatializer for state routines.
841 * Sets variables. Generates filename. Returns np_state_key. die with
842 * UNKNOWN if exception
843 */
844state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
845 char **argv) {
846 state_key *this_state = (state_key *)calloc(1, sizeof(state_key));
847 if (this_state == NULL) {
848 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
849 }
850
851 char *temp_keyname = NULL;
852 if (keyname == NULL) {
853 temp_keyname = _np_state_generate_key(argc, argv);
854 } else {
855 temp_keyname = strdup(keyname);
856 if (temp_keyname == NULL) {
857 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
858 }
859 }
860
861 /* Die if invalid characters used for keyname */
862 char *tmp_char = temp_keyname;
863 while (*tmp_char != '\0') {
864 if (!(isalnum(*tmp_char) || *tmp_char == '_')) {
865 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
866 }
867 tmp_char++;
868 }
869 this_state->name = temp_keyname;
870 this_state->plugin_name = plugin_name;
871 this_state->data_version = expected_data_version;
872 this_state->state_data = NULL;
873
874 /* Calculate filename */
875 char *temp_filename = NULL;
876 int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(),
877 (unsigned long)geteuid(), plugin_name, this_state->name);
878 if (error < 0) {
879 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
880 }
881
882 this_state->_filename = temp_filename;
883
884 return *this_state;
885}
886
887/*
888 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
889 * hopefully a unique key per service/plugin invocation. Use the extra-opts
890 * parse of argv, so that uniqueness in parameters are reflected there.
891 */
892char *_np_state_generate_key(int argc, char **argv) {
893 unsigned char result[256];
894
895#ifdef USE_OPENSSL
896 /*
897 * This code path is chosen if openssl is available (which should be the most common
898 * scenario). Alternatively, the gnulib implementation/
899 *
900 */
901 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
902
903 EVP_DigestInit(ctx, EVP_sha256());
904
905 for (int i = 0; i < argc; i++) {
906 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
907 }
908
909 EVP_DigestFinal(ctx, result, NULL);
910#else
911
912 struct sha256_ctx ctx;
913
914 for (int i = 0; i < this_monitoring_plugin->argc; i++) {
915 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
916 }
917
918 sha256_finish_ctx(&ctx, result);
919#endif // FOUNDOPENSSL
920
921 char keyname[41];
922 for (int i = 0; i < 20; ++i) {
923 sprintf(&keyname[2 * i], "%02x", result[i]);
924 }
925
926 keyname[40] = '\0';
927
928 char *keyname_copy = strdup(keyname);
929 if (keyname_copy == NULL) {
930 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
931 }
932
933 return keyname_copy;
934}
diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h
new file mode 100644
index 00000000..0f7780b1
--- /dev/null
+++ b/plugins/check_snmp.d/check_snmp_helpers.h
@@ -0,0 +1,71 @@
1#pragma once
2
3#include "./config.h"
4#include <net-snmp/library/asn1.h>
5
6check_snmp_test_unit check_snmp_test_unit_init();
7int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool);
8check_snmp_config check_snmp_config_init();
9
10typedef struct {
11 oid oid[MAX_OID_LEN];
12 size_t oid_length;
13 unsigned char type;
14 union {
15 uint64_t uIntVal;
16 int64_t intVal;
17 double doubleVal;
18 } value;
19 char *string_response;
20} response_value;
21
22typedef struct {
23 int errorcode;
24 response_value *response_values;
25} snmp_responces;
26snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters);
27
28// state is similar to response, but only numerics and a timestamp
29typedef struct {
30 time_t timestamp;
31 oid oid[MAX_OID_LEN];
32 size_t oid_length;
33 unsigned char type;
34 union {
35 unsigned long long uIntVal;
36 long long intVal;
37 double doubleVal;
38 } value;
39} check_snmp_state_entry;
40
41typedef struct {
42 check_snmp_state_entry state;
43 mp_subcheck sc;
44} check_snmp_evaluation;
45check_snmp_evaluation evaluate_single_unit(response_value response,
46 check_snmp_evaluation_parameters eval_params,
47 check_snmp_test_unit test_unit, time_t query_timestamp,
48 check_snmp_state_entry prev_state,
49 bool have_previous_state);
50
51#define NP_STATE_FORMAT_VERSION 1
52
53typedef struct state_data_struct {
54 time_t time;
55 void *data;
56 size_t length; /* Of binary data */
57 int errorcode;
58} state_data;
59
60typedef struct state_key_struct {
61 char *name;
62 char *plugin_name;
63 int data_version;
64 char *_filename;
65 state_data *state_data;
66} state_key;
67
68state_data *np_state_read(state_key stateKey);
69state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc,
70 char **argv);
71void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore);
diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h
new file mode 100644
index 00000000..e7b6d1b3
--- /dev/null
+++ b/plugins/check_snmp.d/config.h
@@ -0,0 +1,81 @@
1#pragma once
2
3#include "../../lib/thresholds.h"
4#include "../../lib/states.h"
5#include <stdlib.h>
6#include <stdbool.h>
7#include <regex.h>
8#include "../common.h"
9
10// defines for snmp libs
11#define u_char unsigned char
12#define u_long unsigned long
13#define u_short unsigned short
14#define u_int unsigned int
15
16#include <net-snmp/net-snmp-config.h>
17#include <net-snmp/net-snmp-includes.h>
18#include <net-snmp/library/snmp.h>
19#include <net-snmp/session_api.h>
20
21#define DEFAULT_PORT "161"
22#define DEFAULT_RETRIES 5
23
24typedef struct eval_method {
25 bool crit_string;
26 bool crit_regex;
27} eval_method;
28
29typedef struct check_snmp_test_unit {
30 char *oid;
31 char *label;
32 char *unit_value;
33 eval_method eval_mthd;
34 mp_thresholds threshold;
35} check_snmp_test_unit;
36
37typedef struct {
38 struct snmp_session snmp_session;
39 // use getnet instead of get
40 bool use_getnext;
41
42 // TODO actually make these useful
43 bool ignore_mib_parsing_errors;
44 bool need_mibs;
45
46 check_snmp_test_unit *test_units;
47 size_t num_of_test_units;
48} check_snmp_config_snmp_parameters;
49
50typedef struct {
51 // State if an empty value is encountered
52 mp_state_enum nulloid_result;
53
54 // String evaluation stuff
55 bool invert_search;
56 regex_t regex_cmp_value; // regex to match query results against
57 char string_cmp_value[MAX_INPUT_BUFFER];
58
59 // Modify data
60 double multiplier;
61 bool multiplier_set;
62 double offset;
63 bool offset_set;
64
65 // Modify output
66 bool use_oid_as_perf_data_label;
67
68 // activate rate calculation
69 bool calculate_rate;
70 unsigned int rate_multiplier;
71} check_snmp_evaluation_parameters;
72
73typedef struct check_snmp_config {
74 // SNMP session to use
75 check_snmp_config_snmp_parameters snmp_params;
76
77 check_snmp_evaluation_parameters evaluation_params;
78
79 mp_output_format output_format;
80 bool output_format_is_set;
81} check_snmp_config;
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 9d0d7cde..f6c8d551 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -57,7 +57,8 @@ static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*
57static void print_help(void); 57static void print_help(void);
58void print_usage(void); 58void print_usage(void);
59 59
60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, char *remote_protocol); 60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version,
61 char *remote_protocol);
61 62
62int main(int argc, char **argv) { 63int main(int argc, char **argv) {
63 setlocale(LC_ALL, ""); 64 setlocale(LC_ALL, "");
@@ -85,7 +86,8 @@ int main(int argc, char **argv) {
85 alarm(socket_timeout); 86 alarm(socket_timeout);
86 87
87 /* ssh_connect exits if error is found */ 88 /* ssh_connect exits if error is found */
88 ssh_connect(&overall, config.server_name, config.port, config.remote_version, config.remote_protocol); 89 ssh_connect(&overall, config.server_name, config.port, config.remote_version,
90 config.remote_protocol);
89 91
90 alarm(0); 92 alarm(0);
91 93
@@ -96,19 +98,20 @@ int main(int argc, char **argv) {
96 98
97/* process command-line arguments */ 99/* process command-line arguments */
98process_arguments_wrapper process_arguments(int argc, char **argv) { 100process_arguments_wrapper process_arguments(int argc, char **argv) {
99 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 101 static struct option longopts[] = {
100 {"version", no_argument, 0, 'V'}, 102 {"help", no_argument, 0, 'h'},
101 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 103 {"version", no_argument, 0, 'V'},
102 {"hostname", required_argument, 0, 'H'}, 104 {"host", required_argument, 0, 'H'}, /* backward compatibility */
103 {"port", required_argument, 0, 'p'}, 105 {"hostname", required_argument, 0, 'H'},
104 {"use-ipv4", no_argument, 0, '4'}, 106 {"port", required_argument, 0, 'p'},
105 {"use-ipv6", no_argument, 0, '6'}, 107 {"use-ipv4", no_argument, 0, '4'},
106 {"timeout", required_argument, 0, 't'}, 108 {"use-ipv6", no_argument, 0, '6'},
107 {"verbose", no_argument, 0, 'v'}, 109 {"timeout", required_argument, 0, 't'},
108 {"remote-version", required_argument, 0, 'r'}, 110 {"verbose", no_argument, 0, 'v'},
109 {"remote-protocol", required_argument, 0, 'P'}, 111 {"remote-version", required_argument, 0, 'r'},
110 {"output-format", required_argument, 0, output_format_index}, 112 {"remote-protocol", required_argument, 0, 'P'},
111 {0, 0, 0, 0}}; 113 {"output-format", required_argument, 0, output_format_index},
114 {0, 0, 0, 0}};
112 115
113 process_arguments_wrapper result = { 116 process_arguments_wrapper result = {
114 .config = check_ssh_config_init(), 117 .config = check_ssh_config_init(),
@@ -228,7 +231,8 @@ process_arguments_wrapper process_arguments(int argc, char **argv) {
228 * 231 *
229 *-----------------------------------------------------------------------*/ 232 *-----------------------------------------------------------------------*/
230 233
231int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, char *desired_remote_protocol) { 234int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version,
235 char *desired_remote_protocol) {
232 struct timeval tv; 236 struct timeval tv;
233 gettimeofday(&tv, NULL); 237 gettimeofday(&tv, NULL);
234 238
@@ -238,32 +242,34 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
238 mp_subcheck connection_sc = mp_subcheck_init(); 242 mp_subcheck connection_sc = mp_subcheck_init();
239 if (result != STATE_OK) { 243 if (result != STATE_OK) {
240 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); 244 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
241 xasprintf(&connection_sc.output, "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); 245 xasprintf(&connection_sc.output,
246 "Failed to establish TCP connection to Host %s and Port %d", haddr, hport);
242 mp_add_subcheck_to_check(overall, connection_sc); 247 mp_add_subcheck_to_check(overall, connection_sc);
243 return result; 248 return result;
244 } 249 }
245 250
246 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); 251 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
247 char *buffer = NULL; 252 char *buffer = NULL;
248 size_t recv_ret = 0; 253 ssize_t recv_ret = 0;
249 char *version_control_string = NULL; 254 char *version_control_string = NULL;
250 size_t byte_offset = 0; 255 size_t byte_offset = 0;
251 while ((version_control_string == NULL) && 256 while ((version_control_string == NULL) &&
252 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), 0) > 0)) { 257 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset),
258 0) > 0)) {
253 259
254 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ 260 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/
255 byte_offset = 0; 261 byte_offset = 0;
256 262
257 char *index = NULL; 263 char *index = NULL;
258 unsigned long len = 0;
259 while ((index = strchr(output + byte_offset, '\n')) != NULL) { 264 while ((index = strchr(output + byte_offset, '\n')) != NULL) {
260 /*Partition the buffer so that this line is a separate string, 265 /*Partition the buffer so that this line is a separate string,
261 * by replacing the newline with NUL*/ 266 * by replacing the newline with NUL*/
262 output[(index - output)] = '\0'; 267 output[(index - output)] = '\0';
263 len = strlen(output + byte_offset); 268 size_t len = strlen(output + byte_offset);
264 269
265 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { 270 if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) {
266 /*if the string starts with SSH-, this _should_ be a valid version control string*/ 271 /*if the string starts with SSH-, this _should_ be a valid version control
272 * string*/
267 version_control_string = output + byte_offset; 273 version_control_string = output + byte_offset;
268 break; 274 break;
269 } 275 }
@@ -273,21 +279,23 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
273 } 279 }
274 280
275 if (version_control_string == NULL) { 281 if (version_control_string == NULL) {
276 /* move unconsumed data to beginning of buffer, null rest */ 282 /* move unconsumed data to beginning of buffer */
277 memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); 283 memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
278 memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1);
279 284
280 /*start reading from end of current line chunk on next recv*/ 285 /*start reading from end of current line chunk on next recv*/
281 byte_offset = strlen(output); 286 byte_offset = strlen(output);
287
288 /* NUL the rest of the buffer */
289 memset(output + byte_offset, 0, BUFF_SZ - byte_offset);
282 } 290 }
283 } else { 291 } else {
284 byte_offset += recv_ret; 292 byte_offset += (size_t)recv_ret;
285 } 293 }
286 } 294 }
287 295
288 if (recv_ret < 0) { 296 if (recv_ret < 0) {
289 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); 297 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
290 xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - %s", strerror(errno)); 298 xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno));
291 mp_add_subcheck_to_check(overall, connection_sc); 299 mp_add_subcheck_to_check(overall, connection_sc);
292 return OK; 300 return OK;
293 } 301 }
@@ -333,7 +341,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
333 * "1.x" (e.g., "1.5" or "1.3")." 341 * "1.x" (e.g., "1.5" or "1.3")."
334 * - RFC 4253:5 342 * - RFC 4253:5
335 */ 343 */
336 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ 344 char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") +
345 1; /* (+1 for the '-' separating protoversion from softwareversion) */
337 346
338 /* If there's a space in the version string, whatever's after the space is a comment 347 /* If there's a space in the version string, whatever's after the space is a comment
339 * (which is NOT part of the server name/version)*/ 348 * (which is NOT part of the server name/version)*/
@@ -345,13 +354,15 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
345 mp_subcheck protocol_validity_sc = mp_subcheck_init(); 354 mp_subcheck protocol_validity_sc = mp_subcheck_init();
346 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { 355 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
347 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL); 356 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL);
348 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", version_control_string); 357 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s",
358 version_control_string);
349 mp_add_subcheck_to_check(overall, protocol_validity_sc); 359 mp_add_subcheck_to_check(overall, protocol_validity_sc);
350 return OK; 360 return OK;
351 } 361 }
352 362
353 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK); 363 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
354 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", version_control_string); 364 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s",
365 version_control_string);
355 mp_add_subcheck_to_check(overall, protocol_validity_sc); 366 mp_add_subcheck_to_check(overall, protocol_validity_sc);
356 367
357 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; 368 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
@@ -366,8 +377,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
366 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) { 377 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
367 mp_subcheck remote_version_sc = mp_subcheck_init(); 378 mp_subcheck remote_version_sc = mp_subcheck_init();
368 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL); 379 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
369 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), ssh_server, ssh_proto, 380 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"),
370 desired_remote_version); 381 ssh_server, ssh_proto, desired_remote_version);
371 close(socket); 382 close(socket);
372 mp_add_subcheck_to_check(overall, remote_version_sc); 383 mp_add_subcheck_to_check(overall, remote_version_sc);
373 return OK; 384 return OK;
@@ -385,11 +396,13 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
385 396
386 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) { 397 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) {
387 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL); 398 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL);
388 xasprintf(&protocol_version_sc.output, _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, ssh_proto, 399 xasprintf(&protocol_version_sc.output,
389 desired_remote_protocol); 400 _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server,
401 ssh_proto, desired_remote_protocol);
390 } else { 402 } else {
391 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK); 403 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK);
392 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", ssh_server, ssh_proto); 404 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)",
405 ssh_server, ssh_proto);
393 } 406 }
394 407
395 mp_add_subcheck_to_check(overall, protocol_version_sc); 408 mp_add_subcheck_to_check(overall, protocol_version_sc);
@@ -422,7 +435,8 @@ void print_help(void) {
422 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 435 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
423 436
424 printf(" %s\n", "-r, --remote-version=STRING"); 437 printf(" %s\n", "-r, --remote-version=STRING");
425 printf(" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); 438 printf(" %s\n",
439 _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
426 440
427 printf(" %s\n", "-P, --remote-protocol=STRING"); 441 printf(" %s\n", "-P, --remote-protocol=STRING");
428 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); 442 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)"));
@@ -435,5 +449,6 @@ void print_help(void) {
435 449
436void print_usage(void) { 450void print_usage(void) {
437 printf("%s\n", _("Usage:")); 451 printf("%s\n", _("Usage:"));
438 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n", progname); 452 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n",
453 progname);
439} 454}
diff --git a/plugins/check_users.c b/plugins/check_users.c
index f1e1c39d..cd3bd181 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,100 @@ 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, "%d users currently logged in. This violates the warning threshold", user_wrapper.users);
132 break;
133 case STATE_CRITICAL:
134 xasprintf(&sc_users.output, "%d users currently logged in. This violates the critical threshold", user_wrapper.users);
135 break;
136 default:
137 xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users);
176 } 138 }
177 139
178 return result; 140 mp_add_subcheck_to_check(&overall, sc_users);
141 mp_exit(overall);
179} 142}
180 143
144#define output_format_index CHAR_MAX + 1
145
181/* process command-line arguments */ 146/* process command-line arguments */
182int process_arguments(int argc, char **argv) { 147check_users_config_wrapper process_arguments(int argc, char **argv) {
183 static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, 148 static struct option longopts[] = {{"critical", required_argument, 0, 'c'},
184 {"warning", required_argument, 0, 'w'}, 149 {"warning", required_argument, 0, 'w'},
185 {"version", no_argument, 0, 'V'}, 150 {"version", no_argument, 0, 'V'},
186 {"help", no_argument, 0, 'h'}, 151 {"help", no_argument, 0, 'h'},
152 {"output-format", required_argument, 0, output_format_index},
187 {0, 0, 0, 0}}; 153 {0, 0, 0, 0}};
188 154
189 if (argc < 2) 155 if (argc < 2) {
190 usage("\n"); 156 usage(progname);
157 }
158
159 char *warning_range = NULL;
160 char *critical_range = NULL;
161 check_users_config_wrapper result = {
162 .config = check_users_config_init(),
163 .errorcode = OK,
164 };
191 165
192 int option_char;
193 while (true) { 166 while (true) {
194 int option = 0; 167 int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL);
195 option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option);
196 168
197 if (option_char == -1 || option_char == EOF || option_char == 1) 169 if (counter == -1 || counter == EOF || counter == 1) {
198 break; 170 break;
171 }
199 172
200 switch (option_char) { 173 switch (counter) {
201 case '?': /* print short usage statement if args not parsable */ 174 case '?': /* print short usage statement if args not parsable */
202 usage5(); 175 usage5();
203 case 'h': /* help */ 176 case 'h': /* help */
@@ -212,29 +185,66 @@ int process_arguments(int argc, char **argv) {
212 case 'w': /* warning */ 185 case 'w': /* warning */
213 warning_range = optarg; 186 warning_range = optarg;
214 break; 187 break;
188 case output_format_index: {
189 parsed_output_format parser = mp_parse_output_format(optarg);
190 if (!parser.parsing_success) {
191 // TODO List all available formats here, maybe add anothoer usage function
192 printf("Invalid output format: %s\n", optarg);
193 exit(STATE_UNKNOWN);
194 }
195
196 result.config.output_format_is_set = true;
197 result.config.output_format = parser.output_format;
198 break;
199 }
215 } 200 }
216 } 201 }
217 202
218 option_char = optind; 203 int option_char = optind;
219 204
220 if (warning_range == NULL && argc > option_char) 205 if (warning_range == NULL && argc > option_char) {
221 warning_range = argv[option_char++]; 206 warning_range = argv[option_char++];
207 }
222 208
223 if (critical_range == NULL && argc > option_char) 209 if (critical_range == NULL && argc > option_char) {
224 critical_range = argv[option_char++]; 210 critical_range = argv[option_char++];
211 }
225 212
226 /* this will abort in case of invalid ranges */ 213 // TODO add proper verification for ranges here!
227 set_thresholds(&thlds, warning_range, critical_range); 214 mp_range_parsed tmp;
215 if (warning_range) {
216 tmp = mp_parse_range_string(warning_range);
217 } else {
218 printf("Warning threshold missing\n");
219 print_usage();
220 exit(STATE_UNKNOWN);
221 }
228 222
229 if (!thlds->warning) { 223 if (tmp.error == MP_PARSING_SUCCES) {
230 usage4(_("Warning threshold must be a valid range expression")); 224 result.config.thresholds.warning = tmp.range;
225 result.config.thresholds.warning_is_set = true;
226 } else {
227 printf("Failed to parse warning range: %s", warning_range);
228 exit(STATE_UNKNOWN);
231 } 229 }
232 230
233 if (!thlds->critical) { 231 if (critical_range) {
234 usage4(_("Critical threshold must be a valid range expression")); 232 tmp = mp_parse_range_string(critical_range);
233 } else {
234 printf("Critical threshold missing\n");
235 print_usage();
236 exit(STATE_UNKNOWN);
235 } 237 }
236 238
237 return OK; 239 if (tmp.error == MP_PARSING_SUCCES) {
240 result.config.thresholds.critical = tmp.range;
241 result.config.thresholds.critical_is_set = true;
242 } else {
243 printf("Failed to parse critical range: %s", critical_range);
244 exit(STATE_UNKNOWN);
245 }
246
247 return result;
238} 248}
239 249
240void print_help(void) { 250void print_help(void) {
@@ -257,6 +267,7 @@ void print_help(void) {
257 printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); 267 printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION"));
258 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); 268 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION");
259 printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); 269 printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION"));
270 printf(UT_OUTPUT_FORMAT);
260 271
261 printf(UT_SUPPORT); 272 printf(UT_SUPPORT);
262} 273}
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..a8b168a0
--- /dev/null
+++ b/plugins/check_users.d/users.c
@@ -0,0 +1,167 @@
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, WTSUserName, &username, &size)) {
27 continue;
28 }
29
30 int len = lstrlen(username);
31
32 WTSFreeMemory(username);
33
34 if (len == 0) {
35 continue;
36 }
37
38 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) {
39 result.users++;
40 }
41 }
42
43 WTSFreeMemory(wtsinfo);
44 return result;
45}
46# else // HAVE_WTSAPI32_H
47# error On windows but without the WTSAPI32 lib
48# endif // HAVE_WTSAPI32_H
49
50#else // _WIN32
51
52# include "../../config.h"
53# include <stddef.h>
54
55# ifdef HAVE_LIBSYSTEMD
56# include <systemd/sd-daemon.h>
57# include <systemd/sd-login.h>
58
59get_num_of_users_wrapper get_num_of_users_systemd() {
60 get_num_of_users_wrapper result = {};
61
62 // Test whether we booted with systemd
63 if (sd_booted() > 0) {
64 int users = sd_get_uids(NULL);
65 if (users >= 0) {
66 // Success
67 result.users = users;
68 return result;
69 }
70
71 // Failure! return the error code
72 result.errorcode = users;
73 return result;
74 }
75
76 // Looks like we are not running systemd,
77 // return with error here
78 result.errorcode = NO_SYSTEMD_ERROR;
79 return result;
80}
81# endif
82
83# ifdef HAVE_UTMPX_H
84# include <utmpx.h>
85
86get_num_of_users_wrapper get_num_of_users_utmp() {
87 int users = 0;
88
89 /* get currently logged users from utmpx */
90 setutxent();
91
92 struct utmpx *putmpx;
93 while ((putmpx = getutxent()) != NULL) {
94 if (putmpx->ut_type == USER_PROCESS) {
95 users++;
96 }
97 }
98
99 endutxent();
100
101 get_num_of_users_wrapper result = {
102 .errorcode = 0,
103 .users = users,
104 };
105
106 return result;
107}
108# endif
109
110# ifndef HAVE_WTSAPI32_H
111# ifndef HAVE_LIBSYSTEMD
112# ifndef HAVE_UTMPX_H
113// Fall back option here for the others (probably still not on windows)
114
115# include "../popen.h"
116# include "../common.h"
117# include "../utils.h"
118
119get_num_of_users_wrapper get_num_of_users_who_command() {
120 /* run the command */
121 child_process = spopen(WHO_COMMAND);
122 if (child_process == NULL) {
123 // printf(_("Could not open pipe: %s\n"), WHO_COMMAND);
124 get_num_of_users_wrapper result = {
125 .errorcode = COULD_NOT_OPEN_PIPE,
126 };
127 return result;
128 }
129
130 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
131 if (child_stderr == NULL) {
132 // printf(_("Could not open stderr for %s\n"), WHO_COMMAND);
133 // TODO this error should probably be reported
134 }
135
136 get_num_of_users_wrapper result = {};
137 char input_buffer[MAX_INPUT_BUFFER];
138 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
139 /* increment 'users' on all lines except total user count */
140 if (input_buffer[0] != '#') {
141 result.users++;
142 continue;
143 }
144
145 /* get total logged in users */
146 if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) {
147 break;
148 }
149 }
150
151 /* check STDERR */
152 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
153 // if this fails, something broke and the result can not be relied upon or so is the theorie here
154 result.errorcode = STDERR_COULD_NOT_BE_READ;
155 }
156 (void)fclose(child_stderr);
157
158 /* close the pipe */
159 spclose(child_process);
160
161 return result;
162}
163
164# endif
165# endif
166# endif
167#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/t/check_load.t b/plugins/t/check_load.t
index bba8947c..fc26bb35 100644
--- a/plugins/t/check_load.t
+++ b/plugins/t/check_load.t
@@ -16,28 +16,28 @@ my $successScaledOutput = "/^LOAD OK - scaled load average: $loadValue, $loadVal
16my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/"; 16my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/";
17my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/"; 17my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/";
18 18
19plan tests => 13; 19plan tests => 8;
20 20
21$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" ); 21$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" );
22cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); 22cmp_ok( $res->return_code, 'eq', 0, "load not over 100");
23like( $res->output, $successOutput, "Output OK"); 23# like( $res->output, $successOutput, "Output OK");
24 24
25$res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" ); 25$res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" );
26cmp_ok( $res->return_code, 'eq', 2, "Load over 0"); 26cmp_ok( $res->return_code, 'eq', 2, "Load over 0");
27like( $res->output, $failureOutput, "Output OK"); 27# like( $res->output, $failureOutput, "Output OK");
28 28
29$res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" ); 29$res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" );
30cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); 30cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division");
31like( $res->output, $failurScaledOutput, "Output OK"); 31# like( $res->output, $failurScaledOutput, "Output OK");
32 32
33$res = NPTest->testCmd( "./check_load -w 100 -c 100,110" ); 33$res = NPTest->testCmd( "./check_load -w 100 -c 100,110" );
34cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); 34cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments");
35like( $res->output, $successOutput, "Output OK"); 35# like( $res->output, $successOutput, "Output OK");
36like( $res->perf_output, "/load1=$loadValue;100.000;100.000/", "Test handling of non triplet thresholds (load1)"); 36like( $res->perf_output, "/'load1'=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)");
37like( $res->perf_output, "/load5=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load5)"); 37like( $res->perf_output, "/'load5'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)");
38like( $res->perf_output, "/load15=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load15)"); 38like( $res->perf_output, "/'load15'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load15)");
39 39
40 40
41$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" ); 41$res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" );
42cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); 42cmp_ok( $res->return_code, 'eq', 0, "load not over 100");
43like( $res->output, $successScaledOutput, "Output OK"); 43# like( $res->output, $successScaledOutput, "Output OK");
diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t
index 576cc506..8d435df3 100644
--- a/plugins/t/check_snmp.t
+++ b/plugins/t/check_snmp.t
@@ -10,7 +10,7 @@ use NPTest;
10 10
11BEGIN { 11BEGIN {
12 plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; 12 plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp";
13 plan tests => 63; 13 plan tests => 62;
14} 14}
15 15
16my $res; 16my $res;
@@ -24,7 +24,7 @@ my $user_snmp = getTestParameter("NP_SNMP_USER", "An SNMP user", "auth_
24 24
25$res = NPTest->testCmd( "./check_snmp -t 1" ); 25$res = NPTest->testCmd( "./check_snmp -t 1" );
26is( $res->return_code, 3, "No host name" ); 26is( $res->return_code, 3, "No host name" );
27is( $res->output, "No host specified" ); 27is( $res->output, "No OIDs specified" );
28 28
29$res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" ); 29$res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" );
30is( $res->return_code, 3, "No OIDs specified" ); 30is( $res->return_code, 3, "No OIDs specified" );
@@ -32,145 +32,124 @@ is( $res->output, "No OIDs specified" );
32 32
33$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" ); 33$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" );
34is( $res->return_code, 3, "Invalid seclevel" ); 34is( $res->return_code, 3, "Invalid seclevel" );
35like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); 35like( $res->output, "/invalid security level: rubbish/" );
36 36
37$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" ); 37$res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" );
38is( $res->return_code, 3, "Invalid protocol" ); 38is( $res->return_code, 3, "Invalid protocol" );
39like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); 39like( $res->output, "/invalid SNMP version/protocol: 3c/" );
40 40
41SKIP: { 41SKIP: {
42 skip "no snmp host defined", 50 if ( ! $host_snmp ); 42 skip "no snmp host defined", 50 if ( ! $host_snmp );
43 43
44 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); 44 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -P 2c -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:");
45 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" ); 45 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" );
46 like($res->output, '/^SNMP OK - (\d+)/', "String contains SNMP OK"); 46 $res->output =~ /\|.*=(\d+);/;
47 $res->output =~ /^SNMP OK - (\d+)/;
48 my $value = $1; 47 my $value = $1;
49 cmp_ok( $value, ">", 0, "Got a time value" ); 48 cmp_ok( $value, ">", 0, "Got a time value" );
50 like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it"); 49 like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it");
51 50
52 51
53 # some more threshold tests 52 # some more threshold tests
54 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1"); 53 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1 -P 2c");
55 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" ); 54 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" );
56 55
57 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:"); 56 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1: -P 2c");
58 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" ); 57 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" );
59 58
60 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1"); 59 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1 -P 2c");
61 cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" ); 60 cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" );
62 61
63 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10"); 62 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10 -P 2c");
64 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" ); 63 cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" );
65 64
66 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10"); 65 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10 -P 2c");
67 cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" ); 66 cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" );
68 67
69 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 10:1");
70 cmp_ok( $res->return_code, '==', 0, "Threshold test -c 10:1" );
71
72 68
73 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1:"); 69 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1: -P 2c");
74 cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" ); 70 cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" );
75 like($res->output, '/^SNMP OK - \d+/', "String contains SNMP OK");
76 71
77 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0"); 72 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -P 2c");
78 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" ); 73 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" );
79 unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values"); 74 unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values");
80 75
81 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0"); 76 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0 -P 2c");
82 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" ); 77 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" );
83 like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
84 78
85 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0"); 79 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0 -P 2c");
86 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" ); 80 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" );
87 like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
88 81
89 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1"); 82 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1 -P 2c");
90 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" ); 83 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" );
91 like($res->output, '/^SNMP OK - 1\s.*$/', "String fits SNMP OK and output format");
92 84
93 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1:"); 85 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1: -P 2c");
94 cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " ); 86 cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " );
95 like($res->output, '/^SNMP WARNING - \*1\*\s.*$/', "String matches SNMP WARNING and output format");
96 87
97 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0"); 88 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0 -P 2c");
98 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" ); 89 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" );
99 like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and output format");
100 90
101 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2"); 91 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2 -P 2c");
102 cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" ); 92 cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" );
103 like($res->output, "/^SNMP OK - 2 1/", "Got two values back" ); 93 like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" );
104 like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); 94 like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" );
105 like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" );
106 95
107 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2"); 96 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2 -P 2c");
108 cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" ); 97 cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" );
109 like($res->output, "/^SNMP CRITICAL - 2 *1*/", "Got two values back" ); 98 like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" );
110 like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); 99 like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" );
111 like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" );
112 100
113 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1:"); 101 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1: -P 2c");
114 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses"); 102 cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses");
115 like($res->output, '/^SNMP OK - \d+ \d+/', "String contains hrMemorySize and hrSystemProcesses");
116 103
117 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0"); 104 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0 -P 2c");
118 cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds"); 105 cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds");
119 like($res->output, '/^SNMP OK - 1\s.*$/', "String matches SNMP OK and output format");
120 106
121 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3"); 107 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -P 2c");
122 $res->output =~ m/^SNMP OK - (\d+\.\d{2})\s.*$/; 108 $res->output =~ m/^.*Value: (\d+).*$/;
123 my $lower = $1 - 0.05; 109 my $lower = $1 - 0.05;
124 my $higher = $1 + 0.05; 110 my $higher = $1 + 0.05;
125 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3 -w $lower -c $higher"); 111 # $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -w $lower -c $higher -P 2c");
126 cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractionnal arguments"); 112 # cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractional arguments");
127 113
128 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2"); 114 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2 -P 2c");
129 cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold"); 115 cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold");
130 like($res->output, '/^SNMP WARNING - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s+\*1\*\s.*$/', "First OID returned as string, 2nd checked for thresholds");
131 116
132 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c ''"); 117 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c '' -P 2c");
133 cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash"); 118 cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash");
134 119
135 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2"); 120 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2 -P 2c");
136 cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check"); 121 cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check");
137 like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping first two thresholds, result printed rather than parsed");
138 122
139 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,,"); 123 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,, -P 2c");
140 cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds"); 124 cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds");
141 like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping all thresholds, result printed rather than parsed");
142 125
143 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec'"); 126 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec' -P 2c");
144 cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold"); 127 cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold");
145 like($res->output, '/^SNMP CRITICAL - \*\d+\* 1\/100 sec.*$/', "Timetick used as a threshold, parsed as numeric");
146 128
147 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0"); 129 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -P 2c");
148 cmp_ok( $res->return_code, '==', 0, "Timetick used as a string"); 130 cmp_ok( $res->return_code, '==', 0, "Timetick used as a string");
149 like($res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "Timetick used as a string, result printed rather than parsed");
150 131
151 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1"); 132 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1 -P 2c");
152 cmp_ok( $res->return_code, '==', 0, "snmp response without datatype"); 133 cmp_ok( $res->return_code, '==', 0, "snmp response without datatype");
153 like( $res->output, '/^SNMP OK - "(systemd|init)" \| $/', "snmp response without datatype" );
154} 134}
155 135
156SKIP: { 136SKIP: {
157 skip "no SNMP user defined", 1 if ( ! $user_snmp ); 137 skip "no SNMP user defined", 1 if ( ! $user_snmp );
158 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv"); 138 $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv");
159 like( $res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "noAuthNoPriv security level works properly" );
160} 139}
161 140
162# These checks need a complete command line. An invalid community is used so 141# These checks need a complete command line. An invalid community is used so
163# the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway 142# the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway
164SKIP: { 143SKIP: {
165 skip "no non responsive host defined", 2 if ( ! $host_nonresponsive ); 144 skip "no non responsive host defined", 2 if ( ! $host_nonresponsive );
166 $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); 145 $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c");
167 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" ); 146 cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" );
168 like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); 147 # like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem");
169} 148}
170 149
171SKIP: { 150SKIP: {
172 skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); 151 skip "no non invalid host defined", 2 if ( ! $hostname_invalid );
173 $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); 152 $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c");
174 cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); 153 cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" );
175 like($res->output, '/External command error: .*(nosuchhost|Name or service not known|Unknown host).*/s', "String matches invalid host"); 154 like($res->output, '/.*Unknown host.*/s', "String matches invalid host");
176} 155}
diff --git a/plugins/t/check_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_snmp.t b/plugins/tests/check_snmp.t
index bfe42e16..26d67898 100755
--- a/plugins/tests/check_snmp.t
+++ b/plugins/tests/check_snmp.t
@@ -4,12 +4,13 @@
4# 4#
5 5
6use strict; 6use strict;
7use warnings;
7use Test::More; 8use Test::More;
8use NPTest; 9use NPTest;
9use FindBin qw($Bin); 10use FindBin qw($Bin);
10use POSIX qw/strftime/; 11use POSIX qw/strftime/;
11 12
12my $tests = 81; 13my $tests = 75;
13# Check that all dependent modules are available 14# Check that all dependent modules are available
14eval { 15eval {
15 require NetSNMP::OID; 16 require NetSNMP::OID;
@@ -76,49 +77,36 @@ my $res;
76 77
77$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0"); 78$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0");
78cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); 79cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" );
79like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); 80like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines");
80like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software |
81.1.3.6.1.4.1.8072.3.2.67.0:
82"Cisco Internetwork Operating System Software
83IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version
8412.2(20)EWA, RELEASE SOFTWARE (fc1)
85Technical Support: http://www.cisco.com/techsupport
86Copyright (c) 1986-2004 by cisco Systems, Inc.
87"').'/m', "String contains all lines");
88 81
89# sysContact.0 is "Alice" (from our snmpd.conf) 82# sysContact.0 is "Alice" (from our snmpd.conf)
90$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1"); 83$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1");
91cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); 84cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
92like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); 85# like($res->output, '/^SNMP OK - /', "String contains SNMP OK");
93like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | 86like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines with multiple OIDs");
94.1.3.6.1.4.1.8072.3.2.67.0: 87like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs");
95"Cisco Internetwork Operating System Software 88like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs");
96IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version
9712.2(20)EWA, RELEASE SOFTWARE (fc1)
98Technical Support: http://www.cisco.com/techsupport
99Copyright (c) 1986-2004 by cisco Systems, Inc.
100"
101.1.3.6.1.4.1.8072.3.2.67.1:
102"Kisco Outernetwork Oserating Gystem Totware
103Copyleft (c) 2400-2689 by kisco Systrems, Inc."').'/m', "String contains all lines with multiple OIDs");
104 89
105$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2"); 90$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2");
106like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | 91cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
107.1.3.6.1.4.1.8072.3.2.67.2: 92# like($res->output, '/'.quotemeta('This should not confuse check_snmp \"parser\" |
108"This should not confuse check_snmp \"parser\" 93# .1.3.6.1.4.1.8072.3.2.67.2:
109into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); 94# "This should not confuse check_snmp \"parser\"
95# into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1");
110 96
111$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3"); 97$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3");
112like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | 98cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
113.1.3.6.1.4.1.8072.3.2.67.3: 99# like($res->output, '/'.quotemeta('It\'s getting even harder if the line |
114"It\'s getting even harder if the line 100# .1.3.6.1.4.1.8072.3.2.67.3:
115ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); 101# "It\'s getting even harder if the line
102# ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2");
116 103
117$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4"); 104$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4");
118like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | 105cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" );
119.1.3.6.1.4.1.8072.3.2.67.4: 106# like($res->output, '/'.quotemeta('And now have fun with with this: \"C:\\\\\" |
120"And now have fun with with this: \"C:\\\\\" 107# .1.3.6.1.4.1.8072.3.2.67.4:
121because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); 108# "And now have fun with with this: \"C:\\\\\"
109# because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3");
122 110
123system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); 111system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*");
124 112
@@ -131,156 +119,159 @@ SKIP: {
131 my $ts = time(); 119 my $ts = time();
132 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); 120 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" );
133 is($res->return_code, 0, "Returns OK"); 121 is($res->return_code, 0, "Returns OK");
134 is($res->output, "No previous data to calculate rate - assume okay"); 122 like($res->output, "/.*No previous data to calculate rate - assume okay.*/");
135 123
136 # test rate 1 second later 124 # test rate 1 second later
137 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); 125 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" );
138 is($res->return_code, 1, "WARNING - due to going above rate calculation" ); 126 is($res->return_code, 1, "WARNING - due to going above rate calculation" );
139 is($res->output, "SNMP RATE WARNING - *666* | iso.3.6.1.4.1.8072.3.2.67.10=666;600 "); 127 like($res->output, "/.*=666.*/");
140 128
141 # test rate with same time 129 # test rate with same time
142 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); 130 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" );
143 is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" ); 131 is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" );
144 is($res->output, "Time duration between plugin calls is invalid"); 132 like($res->output, "/.*Time duration between plugin calls is invalid.*/");
145 133
146 134
147 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); 135 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" );
148 is($res->return_code, 0, "OK for first call" ); 136 is($res->return_code, 0, "OK for first call" );
149 is($res->output, "No previous data to calculate rate - assume okay" ); 137 like($res->output, "/.*No previous data to calculate rate - assume okay.*/" );
150 138
151 # test rate 1 second later 139 # test rate 1 second later
152 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); 140 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" );
153 is($res->return_code, 0, "OK as no thresholds" ); 141 is($res->return_code, 0, "OK as no thresholds" );
154 is($res->output, "SNMP RATE OK - inoctets 666 | inoctets=666 ", "Check label"); 142 like($res->output, "/.*inoctets.*=666.*/m", "Check label");
155 143
156 # test rate 3 seconds later 144 # test rate 3 seconds later
157 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); 145 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" );
158 is($res->return_code, 0, "OK as no thresholds" ); 146 is($res->return_code, 0, "OK as no thresholds" );
159 is($res->output, "SNMP RATE OK - inoctets 333 | inoctets=333 ", "Check rate decreases due to longer interval"); 147 like($res->output, "/.*inoctets.*333.*/", "Check rate decreases due to longer interval");
160 148
161 149
162 # label performance data check 150 # label performance data check
163 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" ); 151 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" );
164 is($res->return_code, 0, "OK as no thresholds" ); 152 is($res->return_code, 0, "OK as no thresholds" );
165 is($res->output, "SNMP OK - test 67996 | test=67996c ", "Check label"); 153 like($res->output, "/.*test.?=67996c/", "Check label");
166 154
167 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); 155 # following test is deactivated since it was not valid due to the guidelines (no single quote in label allowed)
168 is($res->return_code, 0, "OK as no thresholds" ); 156 # $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" );
169 is($res->output, "SNMP OK - test'test 68662 | \"test'test\"=68662c ", "Check label"); 157 # is($res->return_code, 0, "OK as no thresholds" );
158 # is($res->output, "test'test 68662 | \"test'test\"=68662c ", "Check label");
170 159
171 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" ); 160 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" );
172 is($res->return_code, 0, "OK as no thresholds" ); 161 is($res->return_code, 0, "OK as no thresholds" );
173 is($res->output, "SNMP OK - test\"test 69328 | 'test\"test'=69328c ", "Check label"); 162 like($res->output, "/.*'test\"test'=68662c.*/", "Check label");
174 163
175 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" ); 164 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" );
176 is($res->return_code, 0, "OK as no thresholds" ); 165 is($res->return_code, 0, "OK as no thresholds" );
177 is($res->output, "SNMP OK - test 69994 | iso.3.6.1.4.1.8072.3.2.67.10=69994c ", "Check label"); 166 like($res->output, "/.*.67.10.?=69328c.*/", "Check label");
178 167
179 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" ); 168 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" );
180 is($res->return_code, 0, "OK as no thresholds" ); 169 is($res->return_code, 0, "OK as no thresholds" );
181 is($res->output, "SNMP OK - 70660 | iso.3.6.1.4.1.8072.3.2.67.10=70660c ", "Check label"); 170 like($res->output, "/.*8072.3.2.67.10.?=69994c.*/", "Check label");
182 171
183 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" ); 172 $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" );
184 is($res->return_code, 0, "OK as no thresholds" ); 173 is($res->return_code, 0, "OK as no thresholds" );
185 is($res->output, "SNMP OK - test test 71326 | 'test test'=71326c ", "Check label"); 174 like($res->output, "/.*'test test'=70660c/", "Check label");
186 175
187 176
188 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); 177 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" );
189 is($res->return_code, 0, "OK for first call" ); 178 is($res->return_code, 0, "OK for first call" );
190 is($res->output, "No previous data to calculate rate - assume okay" ); 179 like($res->output, "/.*No previous data to calculate rate - assume okay.*/" );
191 180
192 # test 1 second later 181 # test 1 second later
193 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); 182 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" );
194 is($res->return_code, 0, "OK as no thresholds" ); 183 is($res->return_code, 0, "OK as no thresholds" );
195 is($res->output, "SNMP RATE OK - inoctets_per_minute 39960 | inoctets_per_minute=39960 ", "Checking multiplier"); 184 like($res->output, "/.*inoctets_per_minute.*=39960/", "Checking multiplier");
196}; 185};
197 186
198 187
199 188
200$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s '\"stringtests\"'" ); 189$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s 'stringtests'" );
201is($res->return_code, 0, "OK as string matches" ); 190is($res->return_code, 0, "OK as string matches" );
202is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); 191like($res->output, '/.*stringtests.*/', "Good string match" );
203 192
204$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" ); 193$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" );
205is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); 194is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" );
206is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); 195like($res->output, '/.*stringtests.*/', "Failed string match" );
207 196
208$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s '\"stringtests\"'" ); 197$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s 'stringtests'" );
209is($res->return_code, 2, "CRITICAL as string matches but inverted" ); 198is($res->return_code, 2, "CRITICAL as string matches but inverted" );
210is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); 199like($res->output, '/.*"stringtests".*/', "Inverted string match" );
211 200
212$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" ); 201$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" );
213is($res->return_code, 0, "OK as string doesn't match but inverted" ); 202is($res->return_code, 0, "OK as string doesn't match but inverted" );
214is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); 203like($res->output, '/.*"stringtests".*/', "OK as inverted string no match" );
215 204
216$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" ); 205$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" );
217is($res->return_code, 1, "Numeric in string test" ); 206# a string is a string and not a number
218is($res->output, 'SNMP WARNING - *3.5* | iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5 ', "WARNING threshold checks for string masquerading as number" ); 207is($res->return_code, 0, "Numeric in string test" );
208like($res->output, '/.*3.5.*| iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5/', "WARNING threshold checks for string masquerading as number" );
219 209
220$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" ); 210$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" );
221is($res->return_code, 0, "Not really numeric test" ); 211is($res->return_code, 0, "Not really numeric test" );
222is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); 212like($res->output, '/.*"87.4startswithnumberbutshouldbestring".*/', "Check string with numeric start is still string" );
223 213
224$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" ); 214$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" );
225is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); 215is($res->return_code, 0, "Not really numeric test (trying best to fool it)" );
226is($res->output, 'SNMP OK - "555\"I said\"" | ', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); 216like($res->output, '/.*\'555"I said"\'.*/', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" );
227 217
228$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" ); 218$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" );
229is($res->return_code, 0, "String check should check whole string, not a parsed number" ); 219is($res->return_code, 0, "String check should check whole string, not a parsed number" );
230is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); 220like($res->output, '/.*CUSTOM CHECK OK: foo is 12345.*/', "String check with numbers returns whole string");
231 221
232$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); 222$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" );
233is($res->return_code, 0, "Negative integer check OK" ); 223is($res->return_code, 0, "Negative integer check OK" );
234is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); 224like($res->output, '/.*-2.*| iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3:/', "Negative integer check OK output" );
235 225
236$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); 226$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" );
237is($res->return_code, 1, "Negative integer check WARNING" ); 227is($res->return_code, 1, "Negative integer check WARNING" );
238is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); 228like($res->output, '/.*-3.*| iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3:/', "Negative integer check WARNING output" );
239 229
240$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); 230$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" );
241is($res->return_code, 2, "Negative integer check CRITICAL" ); 231is($res->return_code, 2, "Negative integer check CRITICAL" );
242is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); 232like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3:/', "Negative integer check CRITICAL output" );
243 233
244$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" ); 234$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" );
245is($res->return_code, 1, "Negative integer as string, WARNING" ); 235is($res->return_code, 1, "Negative integer as string, WARNING" );
246is($res->output, 'SNMP WARNING - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6: ', "Negative integer as string, WARNING output" ); 236like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6:/', "Negative integer as string, WARNING output" );
247 237
248$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" ); 238$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" );
249is($res->return_code, 2, "Negative integer as string, CRITICAL" ); 239is($res->return_code, 2, "Negative integer as string, CRITICAL" );
250is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3: ', "Negative integer as string, CRITICAL output" ); 240like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3:/', "Negative integer as string, CRITICAL output" );
251 241
252$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); 242# deactivated since the perl agent api of snmpd really does not allow floats
253is($res->return_code, 0, "Negative float OK" ); 243# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" );
254is($res->output, 'SNMP OK - -6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); 244# is($res->return_code, 0, "Negative float OK" );
245# is($res->output, '-6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" );
255 246
256$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); 247# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" );
257is($res->return_code, 1, "Negative float WARNING" ); 248# is($res->return_code, 1, "Negative float WARNING" );
258is($res->output, 'SNMP WARNING - *-6.6* | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;@-6.65:~;@-6.55:~ ', "Negative float WARNING output" ); 249# like($res->output, '/-6.6.*| .*67.18=-6.6;@-6.65:~;@-6.55:~/', "Negative float WARNING output" );
259 250
260$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" ); 251$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" );
261is($res->return_code, 0, "Multiple OIDs with thresholds" ); 252is($res->return_code, 0, "Multiple OIDs with thresholds" );
262like($res->output, '/SNMP OK - \d+ -4 | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); 253like($res->output, '/-4.*| .*67.10=\d+c;1:100000;2:200000 .*67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" );
263 254
264$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" ); 255$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" );
265is($res->return_code, 1, "Multiple OIDs with thresholds" ); 256is($res->return_code, 1, "Multiple OIDs with thresholds" );
266like($res->output, '/SNMP WARNING - \d+ \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); 257like($res->output, '/-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" );
267 258
268$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1" ); 259$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1 -O" );
269is($res->return_code, 2, "Multiple OIDs with some thresholds" ); 260is($res->return_code, 2, "Multiple OIDs with some thresholds" );
270like($res->output, '/SNMP CRITICAL - \*\d+\* \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); 261like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" );
271 262
272$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19"); 263$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19");
273is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); 264is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" );
274is($res->output,'SNMP OK - 42 | iso.3.6.1.4.1.8072.3.2.67.19=42 ', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); 265like($res->output,'/.*42.*| iso.3.6.1.4.1.8072.3.2.67.19=42/', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" );
275 266
276$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1"); 267$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1");
277is($res->return_code, 0, "Test multiply RC" ); 268is($res->return_code, 0, "Test multiply RC" );
278is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); 269like($res->output,'/.*4.200000.*| iso.3.6.1.4.1.8072.3.2.67.19=4.200000/' , "Test multiply .1 output" );
279 270
280$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' "); 271$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1");
281is($res->return_code, 0, "Test multiply RC + format" ); 272is($res->return_code, 0, "Test multiply RC" );
282is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); 273like($res->output, '/.*4.20.*| iso.3.6.1.4.1.8072.3.2.67.19=4.20/', "Test multiply .1 output" );
283 274
284$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' -w 1"); 275$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -w 1");
285is($res->return_code, 1, "Test multiply RC + format + thresholds" ); 276is($res->return_code, 1, "Test multiply RC + thresholds" );
286is($res->output, 'SNMP WARNING - *4.20* | iso.3.6.1.4.1.8072.3.2.67.19=4.20;1 ', "Test multiply .1 output + format + thresholds" ); 277like($res->output, '/.*4.20.* | iso.3.6.1.4.1.8072.3.2.67.19=4.20+;1/', "Test multiply .1 output + thresholds" );
diff --git a/plugins/tests/check_snmp_agent.pl b/plugins/tests/check_snmp_agent.pl
index 38912e98..608b6f92 100644
--- a/plugins/tests/check_snmp_agent.pl
+++ b/plugins/tests/check_snmp_agent.pl
@@ -4,9 +4,10 @@
4# 4#
5 5
6#use strict; # Doesn't work 6#use strict; # Doesn't work
7use warnings;
7use NetSNMP::OID qw(:all); 8use NetSNMP::OID qw(:all);
8use NetSNMP::agent; 9use NetSNMP::agent;
9use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); 10use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64 ASN_FLOAT);
10#use Math::Int64 qw(uint64); # Skip that module while we don't need it 11#use Math::Int64 qw(uint64); # Skip that module while we don't need it
11sub uint64 { return $_ } 12sub uint64 { return $_ }
12 13
@@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version
22Technical Support: http://www.cisco.com/techsupport 23Technical Support: http://www.cisco.com/techsupport
23Copyright (c) 1986-2004 by cisco Systems, Inc. 24Copyright (c) 1986-2004 by cisco Systems, Inc.
24'; 25';
25my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware 26my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware
26Copyleft (c) 2400-2689 by kisco Systrems, Inc."; 27Copyleft (c) 2400-2689 by kisco Systrems, Inc.";
27my $multilin3 = 'This should not confuse check_snmp "parser" 28my $multiline3 = 'This should not confuse check_snmp "parser"
28into thinking there is no 2nd line'; 29into thinking there is no 2nd line';
29my $multilin4 = 'It\'s getting even harder if the line 30my $multiline4 = 'It\'s getting even harder if the line
30ends with with this: C:\\'; 31ends with with this: C:\\';
31my $multilin5 = 'And now have fun with with this: "C:\\" 32my $multiline5 = 'And now have fun with with this: "C:\\"
32because we\'re not done yet!'; 33because we\'re not done yet!';
33 34
34# Next are arrays of indexes (Type, initial value and increments) 35# Next are arrays of indexes (Type, initial value and increments)
35# 0..19 <---- please update comment when adding/removing fields 36# 0..19 <---- please update comment when adding/removing fields
36my @fields = (ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_UNSIGNED, ASN_UNSIGNED, ASN_COUNTER, ASN_COUNTER64, ASN_UNSIGNED, ASN_COUNTER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER ); 37my @fields = (ASN_OCTET_STR, # 0
37my @values = ($multiline, $multilin2, $multilin3, $multilin4, $multilin5, 4294965296, 1000, 4294965296, uint64("18446744073709351616"), int(rand(2**32)), 64000, "stringtests", "3.5", "87.4startswithnumberbutshouldbestring", '555"I said"', 'CUSTOM CHECK OK: foo is 12345', -2, '-4', '-6.6', 42 ); 38 ASN_OCTET_STR, # 1
39 ASN_OCTET_STR, # 2
40 ASN_OCTET_STR, # 3
41 ASN_OCTET_STR, # 4
42 ASN_UNSIGNED, # 5
43 ASN_UNSIGNED, # 6
44 ASN_COUNTER, # 7
45 ASN_COUNTER64, # 8
46 ASN_UNSIGNED, # 9
47 ASN_COUNTER, # 10
48 ASN_OCTET_STR, # 11
49 ASN_OCTET_STR, # 12
50 ASN_OCTET_STR, # 13
51 ASN_OCTET_STR, # 14
52 ASN_OCTET_STR, # 15
53 ASN_INTEGER, # 16
54 ASN_INTEGER, # 17
55 ASN_FLOAT, # 18
56 ASN_INTEGER # 19
57 );
58my @values = ($multiline, # 0
59 $multiline2, # 1
60 $multiline3, # 2
61 $multiline4, # 3
62 $multiline5, # 4
63 4294965296, # 5
64 1000, # 6
65 4294965296, # 7
66 uint64("18446744073709351616"), # 8
67 int(rand(2**32)), # 9
68 64000, # 10
69 "stringtests", # 11
70 "3.5", # 12
71 "87.4startswithnumberbutshouldbestring", # 13
72 '555"I said"', # 14
73 'CUSTOM CHECK OK: foo is 12345', # 15
74 '-2', # 16
75 '-4', # 17
76 '-6.6', # 18
77 42 # 19
78 );
38# undef increments are randomized 79# undef increments are randomized
39my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); 80my @incrts = (
81 undef, # 0
82 undef, # 1
83 undef, # 2
84 undef, # 3
85 undef, # 4
86 1000, # 5
87 -500, # 6
88 1000, # 7
89 100000, # 8
90 undef, # 9
91 666, # 10
92 undef, # 11
93 undef, # 12
94 undef, # 13
95 undef, # 14
96 undef, # 15
97 -1, # 16
98 0, # 17
99 undef, # 18
100 0 # 19
101 );
40 102
41# Number of elements in our OID 103# Number of elements in our OID
42my $oidelts; 104my $oidelts;
diff --git a/plugins/tests/conf/snmpd.conf b/plugins/tests/conf/snmpd.conf
index eff5b0b3..1724c027 100644
--- a/plugins/tests/conf/snmpd.conf
+++ b/plugins/tests/conf/snmpd.conf
@@ -19,5 +19,5 @@ syscontact Alice
19# Embedded Subagents 19# Embedded Subagents
20############################################################################### 20###############################################################################
21 21
22perl do "tests/check_snmp_agent.pl"; 22perl do "./tests/check_snmp_agent.pl";
23 23
diff --git a/plugins/tests/test_check_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/utils.h b/plugins/utils.h
index 92a6c115..1d3c153c 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -76,7 +76,7 @@ char *strnl(char *);
76char *strpcpy(char *, const char *, const char *); 76char *strpcpy(char *, const char *, const char *);
77char *strpcat(char *, const char *, const char *); 77char *strpcat(char *, const char *, const char *);
78int xvasprintf(char **strp, const char *fmt, va_list ap); 78int xvasprintf(char **strp, const char *fmt, va_list ap);
79int xasprintf(char **strp, const char *fmt, ...); 79int xasprintf(char **strp, const char *fmt, ...)__attribute__ ((format (printf, 2, 3)));
80 80
81void usage(const char *) __attribute__((noreturn)); 81void usage(const char *) __attribute__((noreturn));
82void usage2(const char *, const char *) __attribute__((noreturn)); 82void usage2(const char *, const char *) __attribute__((noreturn));